Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b7d0fbab3 | ||
|
|
ec567d8a77 | ||
|
|
33489feca8 | ||
|
|
ce82d88215 | ||
|
|
d1ad0815fe | ||
|
|
fc3f1e7c54 | ||
|
|
466658ee7e | ||
|
|
cf18922d19 | ||
|
|
3c081dfd93 | ||
|
|
a135c9cffc | ||
|
|
f891b24cb5 | ||
|
|
70aa3b04f8 | ||
|
|
c24921d56b | ||
|
|
aaec2aba38 | ||
|
|
3b95dd47d5 | ||
|
|
555ecbb70e | ||
|
|
919707efb7 | ||
|
|
c1de8cdc38 | ||
|
|
0cc440f291 | ||
|
|
0524907c18 | ||
|
|
c971fefd41 | ||
|
|
9ed289ef90 | ||
|
|
f3a003e550 | ||
|
|
48c2e9d67d | ||
|
|
0ffaf653a2 | ||
|
|
76d1b430b0 | ||
|
|
ec27d573f4 | ||
|
|
72d3c5ef00 | ||
|
|
81109dcbb6 | ||
|
|
fa510bd00c | ||
|
|
c21c26472c | ||
|
|
d6e7190836 | ||
|
|
d1a46c2c32 | ||
|
|
a02f3a5dd4 | ||
|
|
17b4fb02b3 | ||
|
|
252dcdcc99 | ||
|
|
97fc4f4a85 | ||
|
|
ad6e7c31df | ||
|
|
bf6b981367 | ||
|
|
72ded5ccef | ||
|
|
0344abd67a | ||
|
|
56fefde7e2 | ||
|
|
955c6843b6 | ||
|
|
d1528e51d5 | ||
|
|
53c720d412 | ||
|
|
878f12885b | ||
|
|
0a1a5707bf | ||
|
|
dd08018e9e | ||
|
|
56d04c6570 | ||
|
|
96eb480299 | ||
|
|
14bb3d06a2 | ||
|
|
1a1ac76d2a | ||
|
|
d93a9be656 | ||
|
|
dda16ee1fb | ||
|
|
78112c8582 | ||
|
|
ff778f6d68 | ||
|
|
cee9aeaf6b | ||
|
|
eb66038d4f | ||
|
|
1ee8ce75f1 | ||
|
|
0121d00478 | ||
|
|
e157d005ba | ||
|
|
251107076b | ||
|
|
750c29766f | ||
|
|
b0b67cdf5d | ||
|
|
50f8548b2f | ||
|
|
0ab6031d76 | ||
|
|
667b968939 | ||
|
|
e1128c8b28 | ||
|
|
e3de309804 | ||
|
|
9ca312b4b1 | ||
|
|
d27d91715f | ||
|
|
0eb45343ca | ||
|
|
7e7ef2f01d | ||
|
|
76bfd5b4a2 | ||
|
|
8d414e2fe7 | ||
|
|
2483dab54d | ||
|
|
3a7a2385df | ||
|
|
3274fabeb3 | ||
|
|
e0f8d79772 | ||
|
|
da496ae2fe | ||
|
|
184426175f | ||
|
|
1f9c4f19cf | ||
|
|
8b291aff96 | ||
|
|
8d004f07a4 | ||
|
|
14a2993796 | ||
|
|
3804376ccc | ||
|
|
bddd1e9e3b | ||
|
|
9e7069fde2 | ||
|
|
94b6a1054e | ||
|
|
d714c8380a | ||
|
|
b7ba2e2c57 | ||
|
|
52de5ea6ad | ||
|
|
78caf6ae86 | ||
|
|
46c499d02a | ||
|
|
dbbde6161d | ||
|
|
a62dcdd5f4 | ||
|
|
f7124be091 | ||
|
|
6499224888 | ||
|
|
252d5b319a | ||
|
|
280251656c | ||
|
|
aa796c23db | ||
|
|
0a41c6f0ec | ||
|
|
66aa54d319 | ||
|
|
288eaacd85 | ||
|
|
ecb776f88e | ||
|
|
1c593f1497 | ||
|
|
b4804a4b26 | ||
|
|
5015759334 | ||
|
|
3c292395ca | ||
|
|
8e654d3f78 | ||
|
|
0e245b3752 | ||
|
|
6fb926a560 | ||
|
|
8f33c65fe0 | ||
|
|
beae60bcd5 | ||
|
|
29e42b78c0 | ||
|
|
54fbda2ba4 | ||
|
|
e694d67366 | ||
|
|
6890de958d | ||
|
|
c855ab5624 | ||
|
|
3ccac74de2 | ||
|
|
ee283843fa | ||
|
|
291f301c10 | ||
|
|
e448e10fd7 | ||
|
|
e72ca08554 | ||
|
|
e87f705497 | ||
|
|
93db4233af | ||
|
|
fe993a6712 | ||
|
|
69bc7e34b9 | ||
|
|
a9ed9e329c | ||
|
|
8a21cb1cd3 | ||
|
|
bd87598af9 | ||
|
|
f4d62da8f0 | ||
|
|
ae7212963e | ||
|
|
24f34c9e8e | ||
|
|
98c4ab6d13 | ||
|
|
4a07205963 | ||
|
|
7c5f41ff5f | ||
|
|
a2a2cdb5bc | ||
|
|
41bf41e416 | ||
|
|
ebb43f5176 | ||
|
|
aff6e8e8da | ||
|
|
1b7e8ff6c5 | ||
|
|
739452361f | ||
|
|
ee63c9c210 | ||
|
|
b4491c00c4 | ||
|
|
12cd48a8a9 | ||
|
|
31316e9ca0 | ||
|
|
853fe788f8 | ||
|
|
1dd7b7bddc | ||
|
|
15ef9f9ee6 | ||
|
|
ad2ab5893e | ||
|
|
144571da9b | ||
|
|
8aa8417aa3 | ||
|
|
1fa674c109 | ||
|
|
710872bc22 | ||
|
|
1ff32236fa | ||
|
|
2a7811176c | ||
|
|
2e5df94f0f | ||
|
|
0c494c238a | ||
|
|
375886c777 | ||
|
|
0aa548457f | ||
|
|
e0cb65e8ac | ||
|
|
6875e6a779 | ||
|
|
ac5538c7df | ||
|
|
0ae8aebb89 | ||
|
|
361040f9d9 | ||
|
|
d2281935b3 | ||
|
|
0a6c334b59 | ||
|
|
38b747bb67 | ||
|
|
993bee38ca | ||
|
|
0b87bf9b72 | ||
|
|
456366b63b | ||
|
|
ec356d3efe | ||
|
|
5786d07450 | ||
|
|
8d8a2045bd | ||
|
|
0e9cfa6b64 | ||
|
|
d47dff3faa | ||
|
|
b81d9c79a8 | ||
|
|
57831d52dc | ||
|
|
b14c9eb748 | ||
|
|
cb1c072534 | ||
|
|
c17ff1703a | ||
|
|
65ad85790a | ||
|
|
17cd6798a6 | ||
|
|
d8d4e18835 | ||
|
|
009dfbe3b6 | ||
|
|
2ed9370bd6 | ||
|
|
e0c005cb75 | ||
|
|
4d6c1be7b6 | ||
|
|
f8c57433b9 | ||
|
|
ead2933792 | ||
|
|
79f8ee2808 | ||
|
|
e3e6c1939f | ||
|
|
4d747400bb | ||
|
|
8838c4a5b1 | ||
|
|
a75e933dac | ||
|
|
ab7ac46bcc | ||
|
|
7a19eb490e | ||
|
|
8782354e6d | ||
|
|
f287ccf017 | ||
|
|
2f4e01e772 | ||
|
|
9d2bc9a4ac | ||
|
|
3586e6ceeb | ||
|
|
21e5e4da4b | ||
|
|
bad743aed5 | ||
|
|
126367bb09 | ||
|
|
1e53308d65 | ||
|
|
fea1e09a9c | ||
|
|
7020908691 | ||
|
|
876e93d8b7 | ||
|
|
4e0e24c3c2 | ||
|
|
f095e86b61 | ||
|
|
c2ee824c26 | ||
|
|
d96f6edf49 | ||
|
|
e76f503fff | ||
|
|
1557d2d470 | ||
|
|
7eacfdcd25 | ||
|
|
09c61e0a45 | ||
|
|
b7144ad8bf | ||
|
|
269f271b73 | ||
|
|
fdf6e5f5aa | ||
|
|
e9822f6012 | ||
|
|
e721660e6d | ||
|
|
60a617df31 | ||
|
|
e45f469280 | ||
|
|
dc04541ba8 | ||
|
|
9e04e31906 | ||
|
|
a8eae12af5 | ||
|
|
e2139488ff | ||
|
|
f1205293a7 | ||
|
|
2492b5f119 | ||
|
|
d3bd5c9541 | ||
|
|
b4abcbd19d | ||
|
|
97363e176d | ||
|
|
95d5bee832 | ||
|
|
eb56807b41 | ||
|
|
7929e36e93 | ||
|
|
44eda2177d | ||
|
|
deebc55014 | ||
|
|
944fa1214a | ||
|
|
37544e53dc | ||
|
|
f622f9d3a3 | ||
|
|
ab6f6aa61e | ||
|
|
af0b903afa | ||
|
|
4eae4a5da7 | ||
|
|
0eb72d786c | ||
|
|
8a9c9e0ede | ||
|
|
cd32179627 | ||
|
|
f3b00ec142 | ||
|
|
7c73f70b89 | ||
|
|
392ae68e5f | ||
|
|
a6694b7b96 | ||
|
|
23f6f3f50c | ||
|
|
215afe7a41 | ||
|
|
d04d2380e3 | ||
|
|
091bfa3abf |
No files matched your search
@@ -32,14 +32,20 @@ API changes
|
||||
|
||||
::
|
||||
|
||||
... - add "GL_MP_D3D_interfaces" pseudo extension to make it possible to
|
||||
--- mpv 0.12.0 ---
|
||||
1.20 - deprecate "GL_MP_D3D_interfaces"/"glMPGetD3DInterface", and introduce
|
||||
"GL_MP_MPGetNativeDisplay"/"glMPGetNativeDisplay" (this is a
|
||||
backwards-compatible rename)
|
||||
--- mpv 0.11.0 ---
|
||||
--- mpv 0.10.0 ---
|
||||
1.19 - add "GL_MP_D3D_interfaces" pseudo extension to make it possible to
|
||||
use DXVA2 in OpenGL fullscreen mode in some situations
|
||||
1.19 - mpv_request_log_messages() now accepts "terminal-default" as parameter
|
||||
- mpv_request_log_messages() now accepts "terminal-default" as parameter
|
||||
1.18 - add MPV_END_FILE_REASON_REDIRECT, and change behavior of
|
||||
MPV_EVENT_END_FILE accordingly
|
||||
- a bunch of interface-changes.rst changes
|
||||
1.17 - mpv_initialize() now blocks SIGPIPE (details see client.h)
|
||||
--- mpv 0.9.0 is released ---
|
||||
--- mpv 0.9.0 ---
|
||||
1.16 - add mpv_opengl_cb_report_flip()
|
||||
- introduce mpv_opengl_cb_draw() and deprecate mpv_opengl_cb_render()
|
||||
- add MPV_FORMAT_BYTE_ARRAY
|
||||
@@ -48,7 +54,7 @@ API changes
|
||||
mpv.conf.
|
||||
- minor backwards-compatible change to the "seek" and "screenshot"
|
||||
commands (new flag syntax, old additional args deprecated)
|
||||
--- mpv 0.8.0 is released ---
|
||||
--- mpv 0.8.0 ---
|
||||
1.14 - add mpv_wait_async_requests()
|
||||
- the --msg-level option changes its native type from a flat string to
|
||||
a key-value list (setting/reading the option as string still works)
|
||||
@@ -61,7 +67,7 @@ API changes
|
||||
its own and mpv's OpenGL rendering
|
||||
Warning: this API is not stable yet - anything in opengl_cb.h might
|
||||
be changed in completely incompatible ways in minor API bumps
|
||||
--- mpv 0.7.0 is released ---
|
||||
--- mpv 0.7.0 ---
|
||||
1.10 - deprecate/disable everything directly related to script_dispatch
|
||||
(most likely affects nobody)
|
||||
1.9 - add enum mpv_end_file_reason for mpv_event_end_file.reason
|
||||
@@ -84,7 +90,7 @@ API changes
|
||||
1.6 - modify "core-idle" property behavior
|
||||
- MPV_EVENT_LOG_MESSAGE now always sends complete lines
|
||||
- introduce numeric log levels (mpv_log_level)
|
||||
--- mpv 0.6.0 is released ---
|
||||
--- mpv 0.6.0 ---
|
||||
1.5 - change in X11 and "--wid" behavior again. The previous change didn't
|
||||
work as expected, and now the behavior can be explicitly controlled
|
||||
with the "input-x11-keyboard" option. This is only a temporary
|
||||
@@ -93,7 +99,7 @@ API changes
|
||||
although the old option name still works.
|
||||
1.4 - subtle change in X11 and "--wid" behavior
|
||||
(this change was added to 0.5.2, and broke some things, see #1090)
|
||||
--- mpv 0.5.0 is released ---
|
||||
--- mpv 0.5.0 ---
|
||||
1.3 - add MPV_MAKE_VERSION()
|
||||
1.2 - remove "stream-time-pos" property (no replacement)
|
||||
1.1 - remap dvdnav:// to dvd://
|
||||
@@ -105,6 +111,6 @@ API changes
|
||||
insignificant is now significant
|
||||
- add "cache-free" and "cache-used" properties
|
||||
- OSX: the "coreaudio" AO spdif code is split into a separate AO
|
||||
--- mpv 0.4.0 is released ---
|
||||
--- mpv 0.4.0 ---
|
||||
1.0 - the API is declared stable
|
||||
|
||||
@@ -1,35 +1,114 @@
|
||||
Client API examples
|
||||
===================
|
||||
# Client API examples
|
||||
|
||||
All these examples use the mpv client API through libmpv.
|
||||
|
||||
cocoa
|
||||
-----
|
||||
## Where are the docs?
|
||||
|
||||
The libmpv C API is documented directly in the header files (on normal Unix
|
||||
systems, this is in `/usr/include/mpv/client.h`.
|
||||
|
||||
libmpv merely gives you access to mpv's command interface, which is documented
|
||||
here:
|
||||
* Options (`mpv_set_option()` and friends): http://mpv.io/manual/master/#options
|
||||
* Commands (`mpv_command()` and friends): http://mpv.io/manual/master/#list-of-input-commands
|
||||
* Properties (`mpv_set_property()` and friends): http://mpv.io/manual/master/#properties
|
||||
|
||||
Essentially everything is done with them, including loading a file, retrieving
|
||||
playback progress, and so on.
|
||||
|
||||
## Methods of embedding the video window
|
||||
|
||||
All of these examples concentrate on how to integrate mpv's video renderers
|
||||
with your own GUI. This is generally the hardest part. libmpv enforces a
|
||||
somewhat efficient video output method, rather than e.g. returning a RGBA
|
||||
surface in memory for each frame. The latter would be prohibitively inefficient,
|
||||
because it would require conversion on the CPU. The goal is also not requiring
|
||||
the API users to reinvent their own video rendering/scaling/presentation
|
||||
mechanisms.
|
||||
|
||||
There are currently 2 methods of embedding video.
|
||||
|
||||
### Native window embedding
|
||||
|
||||
This uses the platform's native method of nesting multiple windows. For example,
|
||||
Linux/X11 can nest a window from a completely different process. The nested
|
||||
window can redraw contents on its own, and receive user input if the user
|
||||
interacts with this window.
|
||||
|
||||
libmpv lets you specify a parent window for its own video window via the `wid`
|
||||
option. Then libmpv will create its window with your window as parent, and
|
||||
render its video inside of it.
|
||||
|
||||
This method is highly OS-dependent. Some behavior is OS-specific. There are
|
||||
problems with focusing on X11 (the ancient X11 focus policy mismatches with
|
||||
that of modern UI toolkits - it's normally worked around, but this is not
|
||||
easily possible with raw window embedding). It seems to have stability problems
|
||||
on OSX when using the Qt toolkit.
|
||||
|
||||
### OpenGL embedding
|
||||
|
||||
This method lets you use libmpv's OpenGL renderer directly. You create an
|
||||
OpenGL context, and then use `mpv_opengl_cb_draw()` to render the video on
|
||||
each frame.
|
||||
|
||||
This is more complicated, because libmpv will work directly on your own OpenGL
|
||||
state. It's also not possible to have mpv automatically receive user input.
|
||||
You will have to simulate this with the `mouse`/`keypress`/`keydown`/`keyup`
|
||||
commands.
|
||||
|
||||
You also get much more flexibility. For example, you can actually render your
|
||||
own OSD on top of the video, something that is not possible with raw window
|
||||
embedding.
|
||||
|
||||
### Which one to use?
|
||||
|
||||
Due to the various platform-specific behavior and problems (in particular on
|
||||
OSX), OpenGL embedding is recommended. If you're not comfortable with requiring
|
||||
OpenGL, or want to support "direct" video output such as vdpau (which might
|
||||
win when it comes to performance and energy-saving), you should probably
|
||||
support both methods if possible.
|
||||
|
||||
## List of examples
|
||||
|
||||
### simple
|
||||
|
||||
Very primitive terminal-only example. Shows some most basic API usage.
|
||||
|
||||
### cocoa
|
||||
|
||||
Shows how to embed the mpv video window in Objective-C/Cocoa.
|
||||
|
||||
qt
|
||||
--
|
||||
### cocoa-openglcb
|
||||
|
||||
Similar to the cocoa sample, but shows how to integrate mpv's OpenGL renderer
|
||||
using libmpv's opengl-cb API. Since it does not require complicated interaction
|
||||
with Cocoa elements from different libraries, it's more robust.
|
||||
|
||||
### qt
|
||||
|
||||
Shows how to embed the mpv video window in Qt (using normal desktop widgets).
|
||||
|
||||
qml
|
||||
---
|
||||
### qt_opengl
|
||||
|
||||
Shows how to use mpv's OpenGL video renderer in QtQuick2 with QML.
|
||||
Shows how to use mpv's OpenGL video renderer in Qt. This uses the opengl-cb API
|
||||
for video. Since it does not rely on embedding "foreign" native Windows, it's
|
||||
usually more robust, potentially faster, and it's easier to control how your
|
||||
GUI interacts with the video. You can do your own OpenGL rendering on top of
|
||||
the video as well.
|
||||
|
||||
qml_direct
|
||||
----------
|
||||
### qml
|
||||
|
||||
Shows how to use mpv's OpenGL video renderer in QtQuick2 with QML. Uses the
|
||||
opengl-cb API for video. Since the video is a normal QML element, it's trivial
|
||||
to create OSD overlays with QML-native graphical elements as well.
|
||||
|
||||
### qml_direct
|
||||
|
||||
Alternative example, which typically avoids a FBO indirection. Might be
|
||||
slightly faster, but is less flexible and harder to use.
|
||||
slightly faster, but is less flexible and harder to use. In particular, the
|
||||
video is not a normal QML element. Uses the opengl-cb API for video.
|
||||
|
||||
sdl
|
||||
---
|
||||
### sdl
|
||||
|
||||
Show how to embed the mpv OpenGL renderer in SDL.
|
||||
|
||||
simple
|
||||
------
|
||||
|
||||
Very primitive terminal-only example. Shows some most basic API usage.
|
||||
Show how to embed the mpv OpenGL renderer in SDL. Uses the opengl-cb API for
|
||||
video.
|
||||
@@ -50,7 +50,7 @@ Item {
|
||||
text: "Make video look like on a Smart TV"
|
||||
onClicked: {
|
||||
if (checkbox.checked) {
|
||||
renderer.command(["vo_cmdline", "scale=sharpen3:scale-param1=5.0"])
|
||||
renderer.command(["vo_cmdline", "sharpen=5.0"])
|
||||
} else {
|
||||
renderer.command(["vo_cmdline", ""])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <QApplication>
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
// Qt sets the locale in the QApplication constructor, but libmpv requires
|
||||
// the LC_NUMERIC category to be set to "C", so change it back.
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "mainwindow.h"
|
||||
#include "mpvwidget.h"
|
||||
#include <QPushButton>
|
||||
#include <QSlider>
|
||||
#include <QLayout>
|
||||
#include <QFileDialog>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
m_mpv = new MpvWidget(this);
|
||||
m_slider = new QSlider();
|
||||
m_slider->setOrientation(Qt::Horizontal);
|
||||
m_openBtn = new QPushButton("Open");
|
||||
m_playBtn = new QPushButton("Pause");
|
||||
QHBoxLayout *hb = new QHBoxLayout();
|
||||
hb->addWidget(m_openBtn);
|
||||
hb->addWidget(m_playBtn);
|
||||
QVBoxLayout *vl = new QVBoxLayout();
|
||||
vl->addWidget(m_mpv);
|
||||
vl->addWidget(m_slider);
|
||||
vl->addLayout(hb);
|
||||
setLayout(vl);
|
||||
connect(m_slider, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
|
||||
connect(m_openBtn, SIGNAL(clicked()), SLOT(openMedia()));
|
||||
connect(m_playBtn, SIGNAL(clicked()), SLOT(pauseResume()));
|
||||
connect(m_mpv, SIGNAL(positionChanged(int)), m_slider, SLOT(setValue(int)));
|
||||
connect(m_mpv, SIGNAL(durationChanged(int)), this, SLOT(setSliderRange(int)));
|
||||
}
|
||||
|
||||
void MainWindow::openMedia()
|
||||
{
|
||||
QString file = QFileDialog::getOpenFileName(0, "Open a video");
|
||||
if (file.isEmpty())
|
||||
return;
|
||||
m_mpv->command(QStringList() << "loadfile" << file);
|
||||
}
|
||||
|
||||
void MainWindow::seek(int pos)
|
||||
{
|
||||
m_mpv->command(QVariantList() << "seek" << pos << "absolute");
|
||||
}
|
||||
|
||||
void MainWindow::pauseResume()
|
||||
{
|
||||
const bool paused = m_mpv->getProperty("pause").toBool();
|
||||
m_mpv->setProperty("pause", !paused);
|
||||
}
|
||||
|
||||
void MainWindow::setSliderRange(int duration)
|
||||
{
|
||||
const int time0 = m_mpv->getProperty("time-start").toInt();
|
||||
m_slider->setRange(time0, time0+duration);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef MainWindow_H
|
||||
#define MainWindow_H
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
class MpvWidget;
|
||||
class QSlider;
|
||||
class QPushButton;
|
||||
class MainWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
public Q_SLOTS:
|
||||
void openMedia();
|
||||
void seek(int pos);
|
||||
void pauseResume();
|
||||
private Q_SLOTS:
|
||||
void setSliderRange(int duration);
|
||||
private:
|
||||
MpvWidget *m_mpv;
|
||||
QSlider *m_slider;
|
||||
QPushButton *m_openBtn;
|
||||
QPushButton *m_playBtn;
|
||||
};
|
||||
|
||||
#endif // MainWindow_H
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "mpvwidget.h"
|
||||
#include <stdexcept>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtCore/QMetaObject>
|
||||
|
||||
static void wakeup(void *ctx)
|
||||
{
|
||||
QMetaObject::invokeMethod((MpvWidget*)ctx, "on_mpv_events", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
static void *get_proc_address(void *ctx, const char *name) {
|
||||
Q_UNUSED(ctx);
|
||||
QOpenGLContext *glctx = QOpenGLContext::currentContext();
|
||||
if (!glctx)
|
||||
return NULL;
|
||||
return (void *)glctx->getProcAddress(QByteArray(name));
|
||||
}
|
||||
|
||||
MpvWidget::MpvWidget(QWidget *parent, Qt::WindowFlags f)
|
||||
: QOpenGLWidget(parent, f)
|
||||
{
|
||||
mpv = mpv::qt::Handle::FromRawHandle(mpv_create());
|
||||
if (!mpv)
|
||||
throw std::runtime_error("could not create mpv context");
|
||||
|
||||
mpv_set_option_string(mpv, "terminal", "yes");
|
||||
mpv_set_option_string(mpv, "msg-level", "all=v");
|
||||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("could not initialize mpv context");
|
||||
|
||||
// Make use of the MPV_SUB_API_OPENGL_CB API.
|
||||
mpv::qt::set_option_variant(mpv, "vo", "opengl-cb");
|
||||
|
||||
// Request hw decoding, just for testing.
|
||||
mpv::qt::set_option_variant(mpv, "hwdec", "auto");
|
||||
|
||||
mpv_gl = (mpv_opengl_cb_context *)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
|
||||
if (!mpv_gl)
|
||||
throw std::runtime_error("OpenGL not compiled in");
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, MpvWidget::on_update, (void *)this);
|
||||
connect(this, SIGNAL(frameSwapped()), SLOT(swapped()));
|
||||
|
||||
mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
|
||||
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
mpv_set_wakeup_callback(mpv, wakeup, this);
|
||||
}
|
||||
|
||||
MpvWidget::~MpvWidget()
|
||||
{
|
||||
makeCurrent();
|
||||
if (mpv_gl)
|
||||
mpv_opengl_cb_set_update_callback(mpv_gl, NULL, NULL);
|
||||
// Until this call is done, we need to make sure the player remains
|
||||
// alive. This is done implicitly with the mpv::qt::Handle instance
|
||||
// in this class.
|
||||
mpv_opengl_cb_uninit_gl(mpv_gl);
|
||||
}
|
||||
|
||||
void MpvWidget::command(const QVariant& params)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, params);
|
||||
}
|
||||
|
||||
void MpvWidget::setProperty(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_property_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
QVariant MpvWidget::getProperty(const QString &name) const
|
||||
{
|
||||
return mpv::qt::get_property_variant(mpv, name);
|
||||
}
|
||||
|
||||
void MpvWidget::initializeGL()
|
||||
{
|
||||
int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL);
|
||||
if (r < 0)
|
||||
throw std::runtime_error("could not initialize OpenGL");
|
||||
}
|
||||
|
||||
void MpvWidget::paintGL()
|
||||
{
|
||||
mpv_opengl_cb_draw(mpv_gl, QOpenGLContext::currentContext()->defaultFramebufferObject(), width(), -height());
|
||||
}
|
||||
|
||||
void MpvWidget::swapped()
|
||||
{
|
||||
mpv_opengl_cb_report_flip(mpv_gl, 0);
|
||||
}
|
||||
|
||||
void MpvWidget::on_mpv_events()
|
||||
{
|
||||
// Process all events, until the event queue is empty.
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE) {
|
||||
break;
|
||||
}
|
||||
handle_mpv_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
void MpvWidget::handle_mpv_event(mpv_event *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property *prop = (mpv_event_property *)event->data;
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
Q_EMIT positionChanged(time);
|
||||
}
|
||||
} else if (strcmp(prop->name, "duration") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double *)prop->data;
|
||||
Q_EMIT durationChanged(time);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: ;
|
||||
// Ignore uninteresting or unknown events.
|
||||
}
|
||||
}
|
||||
|
||||
void MpvWidget::on_update(void *ctx)
|
||||
{
|
||||
QMetaObject::invokeMethod((MpvWidget*)ctx, "update");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef PLAYERWINDOW_H
|
||||
#define PLAYERWINDOW_H
|
||||
|
||||
#include <QtWidgets/QOpenGLWidget>
|
||||
#include <mpv/client.h>
|
||||
#include <mpv/opengl_cb.h>
|
||||
#include <mpv/qthelper.hpp>
|
||||
|
||||
class MpvWidget Q_DECL_FINAL: public QOpenGLWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MpvWidget(QWidget *parent = 0, Qt::WindowFlags f = 0);
|
||||
~MpvWidget();
|
||||
void command(const QVariant& params);
|
||||
void setProperty(const QString& name, const QVariant& value);
|
||||
QVariant getProperty(const QString& name) const;
|
||||
QSize sizeHint() const { return QSize(480, 270);}
|
||||
Q_SIGNALS:
|
||||
void durationChanged(int value);
|
||||
void positionChanged(int value);
|
||||
protected:
|
||||
void initializeGL() Q_DECL_OVERRIDE;
|
||||
void paintGL() Q_DECL_OVERRIDE;
|
||||
private Q_SLOTS:
|
||||
void swapped();
|
||||
void on_mpv_events();
|
||||
private:
|
||||
void handle_mpv_event(mpv_event *event);
|
||||
static void on_update(void *ctx);
|
||||
|
||||
mpv::qt::Handle mpv;
|
||||
mpv_opengl_cb_context *mpv_gl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // PLAYERWINDOW_H
|
||||
@@ -0,0 +1,13 @@
|
||||
CONFIG -= app_bundle
|
||||
QT += widgets
|
||||
|
||||
QT_CONFIG -= no-pkg-config
|
||||
CONFIG += link_pkgconfig debug
|
||||
PKGCONFIG += mpv
|
||||
|
||||
HEADERS = \
|
||||
mpvwidget.h \
|
||||
mainwindow.h
|
||||
SOURCES = main.cpp \
|
||||
mpvwidget.cpp \
|
||||
mainwindow.cpp
|
||||
@@ -52,7 +52,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
SDL_Window *window =
|
||||
SDL_CreateWindow("hi", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
1000, 500, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
|
||||
1000, 500, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN |
|
||||
SDL_WINDOW_RESIZABLE);
|
||||
if (!window)
|
||||
die("failed to create SDL window");
|
||||
|
||||
|
||||
@@ -19,7 +19,20 @@ Interface changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.10.0 will be released ---
|
||||
--- mpv 0.12.0 ---
|
||||
- remove --use-text-osd (useless; fontconfig isn't a requirement anymore,
|
||||
and text rendering is also lazily initialized)
|
||||
- some time properties (at least "playback-time", "time-pos",
|
||||
"time-remaining", "playtime-remaining") now are unavailable if the time
|
||||
is unknown, instead of just assuming that the internal playback position
|
||||
is 0
|
||||
- add --audio-fallback-to-null option
|
||||
- replace vf_format outputlevels suboption with "video-output-levels" global
|
||||
property/option; also remove "colormatrix-output-range" property
|
||||
- vo_opengl: remove sharpen3/sharpen5 scale filters, add sharpen sub-option
|
||||
--- mpv 0.11.0 ---
|
||||
- add "af-metadata" property
|
||||
--- mpv 0.10.0 ---
|
||||
- add --video-aspect-method option
|
||||
- add --playlist-pos option
|
||||
- add --video-sync* options
|
||||
@@ -85,4 +98,4 @@ Interface changes
|
||||
- add --screenshot-high-bit-depth
|
||||
- add --screenshot-jpeg-source-chroma
|
||||
- default action for "rescan_external_files" command changes
|
||||
--- mpv 0.9.0 is released ---
|
||||
--- mpv 0.9.0 ---
|
||||
+12
-181
@@ -11,6 +11,10 @@ syntax is:
|
||||
|
||||
To get a full list of available audio filters, see ``--af=help``.
|
||||
|
||||
Also, keep in mind that most actual filters are available via the ``lavfi``
|
||||
wrapper, which gives you access to most of libavfilter's filters. This
|
||||
includes all filters that have been ported from MPlayer to libavfilter.
|
||||
|
||||
You can also set defaults for each filter. The defaults are applied before the
|
||||
normal filter parameters.
|
||||
|
||||
@@ -59,6 +63,13 @@ Available filters are:
|
||||
(If you just want to set defaults for this filter that will be used
|
||||
even by automatically inserted lavrresample instances, you should
|
||||
prefer setting them with ``--af-defaults=lavrresample:...``.)
|
||||
``normalize=<yes|no>``
|
||||
Whether to normalize when remixing channel layouts (default: yes). This
|
||||
is e.g. applied when downmixing surround audio to stereo. The advantage
|
||||
is that this guarantees that no clipping can happen. Unfortunately,
|
||||
this can also lead to too low volume levels. Whether you enable or
|
||||
disable this is essentially a matter of taste, but the default uses
|
||||
the safer choice.
|
||||
``o=<string>``
|
||||
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
|
||||
be documented by FFmpeg or Libav.
|
||||
@@ -96,59 +107,6 @@ Available filters are:
|
||||
If the input channel number is less than ``<minch>``, the filter will
|
||||
detach itself (default: 3).
|
||||
|
||||
``sweep[=speed]``
|
||||
Produces a sine sweep.
|
||||
|
||||
``<0.0-1.0>``
|
||||
Sine function delta, use very low values to hear the sweep.
|
||||
|
||||
``sinesuppress[=freq:decay]``
|
||||
Remove a sine at the specified frequency. Useful to get rid of the 50/60 Hz
|
||||
noise on low quality audio equipment. It only works on mono input.
|
||||
|
||||
``<freq>``
|
||||
The frequency of the sine which should be removed (in Hz) (default:
|
||||
50)
|
||||
``<decay>``
|
||||
Controls the adaptivity (a larger value will make the filter adapt to
|
||||
amplitude and phase changes quicker, a smaller value will make the
|
||||
adaptation slower) (default: 0.0001). Reasonable values are around
|
||||
0.001.
|
||||
|
||||
``bs2b[=option1:option2:...]``
|
||||
Bauer stereophonic to binaural transformation using libbs2b. Improves the
|
||||
headphone listening experience by making the sound similar to that from
|
||||
loudspeakers, allowing each ear to hear both channels and taking into
|
||||
account the distance difference and the head shadowing effect. It is
|
||||
applicable only to 2-channel audio.
|
||||
|
||||
``fcut=<300-1000>``
|
||||
Set cut frequency in Hz.
|
||||
``feed=<10-150>``
|
||||
Set feed level for low frequencies in 0.1*dB.
|
||||
``profile=<value>``
|
||||
Several profiles are available for convenience:
|
||||
|
||||
:default: will be used if nothing else was specified (fcut=700,
|
||||
feed=45)
|
||||
:cmoy: Chu Moy circuit implementation (fcut=700, feed=60)
|
||||
:jmeier: Jan Meier circuit implementation (fcut=650, feed=95)
|
||||
|
||||
If ``fcut`` or ``feed`` options are specified together with a profile, they
|
||||
will be applied on top of the selected profile.
|
||||
|
||||
``hrtf[=flag]``
|
||||
Head-related transfer function: Converts multichannel audio to 2-channel
|
||||
output for headphones, preserving the spatiality of the sound.
|
||||
|
||||
==== ===================================
|
||||
Flag Meaning
|
||||
==== ===================================
|
||||
m matrix decoding of the rear channel
|
||||
s 2-channel matrix decoding
|
||||
0 no matrix decoding (default)
|
||||
==== ===================================
|
||||
|
||||
``equalizer=g1:g2:g3:...:g10``
|
||||
10 octave band graphic equalizer, implemented using 10 IIR band-pass
|
||||
filters. This means that it works regardless of what type of audio is
|
||||
@@ -206,7 +164,7 @@ Available filters are:
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
``mpv --af=channels=4:[0-1,1-0,0-2,1-3] media.avi``
|
||||
``mpv --af=channels=4:[0-1,1-0,2-2,3-3] media.avi``
|
||||
Would change the number of channels to 4 and set up 4 routes that
|
||||
swap channel 0 and channel 1 and leave channel 2 and 3 intact.
|
||||
Observe that if media containing two channels were played back,
|
||||
@@ -354,64 +312,6 @@ Available filters are:
|
||||
``mpv '--af=format=channels=5.1' '--audio-channels=5.1'`` would always force
|
||||
remixing audio to 5.1 and output it like this.
|
||||
|
||||
``sub[=fc:ch]``
|
||||
Adds a subwoofer channel to the audio stream. The audio data used for
|
||||
creating the subwoofer channel is an average of the sound in channel 0 and
|
||||
channel 1. The resulting sound is then low-pass filtered by a 4th order
|
||||
Butterworth filter with a default cutoff frequency of 60Hz and added to a
|
||||
separate channel in the audio stream.
|
||||
|
||||
.. warning::
|
||||
|
||||
Disable this filter when you are playing media with an LFE channel
|
||||
(e.g. 5.1 surround sound), otherwise this filter will disrupt the sound
|
||||
to the subwoofer.
|
||||
|
||||
``<fc>``
|
||||
cutoff frequency in Hz for the low-pass filter (20 Hz to 300 Hz)
|
||||
(default: 60 Hz) For the best result try setting the cutoff frequency
|
||||
as low as possible. This will improve the stereo or surround sound
|
||||
experience.
|
||||
``<ch>``
|
||||
Determines the channel number in which to insert the sub-channel
|
||||
audio. Channel number can be between 0 and 7 (default: 5). Observe
|
||||
that the number of channels will automatically be increased to <ch> if
|
||||
necessary.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=sub=100:4 --audio-channels=5 media.avi``
|
||||
Would add a subwoofer channel with a cutoff frequency of 100 Hz to
|
||||
output channel 4.
|
||||
|
||||
``center``
|
||||
Creates a center channel from the front channels. May currently be low
|
||||
quality as it does not implement a high-pass filter for proper extraction
|
||||
yet, but averages and halves the channels instead.
|
||||
|
||||
``<ch>``
|
||||
Determines the channel number in which to insert the center channel.
|
||||
Channel number can be between 0 and 7 (default: 5). Observe that the
|
||||
number of channels will automatically be increased to ``<ch>`` if
|
||||
necessary.
|
||||
|
||||
``surround[=delay]``
|
||||
Decoder for matrix encoded surround sound like Dolby Surround. Some files
|
||||
with 2-channel audio actually contain matrix encoded surround sound.
|
||||
|
||||
``<delay>``
|
||||
delay time in ms for the rear speakers (0 to 1000) (default: 20) This
|
||||
delay should be set as follows: If d1 is the distance from the
|
||||
listening position to the front speakers and d2 is the distance from
|
||||
the listening position to the rear speakers, then the delay should be
|
||||
set to 15ms if d1 <= d2 and to 15 + 5*(d1-d2) if d1 > d2.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=surround=15 --audio-channels=4 media.avi``
|
||||
Would add surround sound decoding with 15 ms delay for the sound to
|
||||
the rear speakers.
|
||||
|
||||
``delay[=[ch1,ch2,...]]``
|
||||
Delays the sound to the loudspeakers such that the sound from the
|
||||
different channels arrives at the listening position simultaneously. It is
|
||||
@@ -440,36 +340,6 @@ Available filters are:
|
||||
Would delay front left and right by 10.5 ms, the two rear channels
|
||||
and the subwoofer by 0 ms and the center channel by 7 ms.
|
||||
|
||||
``export=mmapped_file:nsamples]``
|
||||
Exports the incoming signal to other processes using memory mapping
|
||||
(``mmap()``). Memory mapped areas contain a header::
|
||||
|
||||
int nch /* number of channels */
|
||||
int size /* buffer size */
|
||||
unsigned long long counter /* Used to keep sync, updated every time
|
||||
new data is exported. */
|
||||
|
||||
The rest is payload (non-interleaved) 16-bit data.
|
||||
|
||||
``<mmapped_file>``
|
||||
File to map data to (required)
|
||||
``<nsamples>``
|
||||
number of samples per channel (default: 512).
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=export=/tmp/mpv-af_export:1024 media.avi``
|
||||
Would export 1024 samples per channel to ``/tmp/mpv-af_export``.
|
||||
|
||||
``extrastereo[=mul]``
|
||||
(Linearly) increases the difference between left and right channels which
|
||||
adds some sort of "live" effect to playback.
|
||||
|
||||
``<mul>``
|
||||
Sets the difference coefficient (default: 2.5). 0.0 means mono sound
|
||||
(average of both channels), with 1.0 sound will be unchanged, with
|
||||
-1.0 left and right channels will be swapped.
|
||||
|
||||
``drc[=method:target]``
|
||||
Applies dynamic range compression. This maximizes the volume by compressing
|
||||
the audio signal's dynamic range. (Formerly called ``volnorm``.)
|
||||
@@ -493,45 +363,6 @@ Available filters are:
|
||||
This filter can cause distortion with audio signals that have a very
|
||||
large dynamic range.
|
||||
|
||||
``ladspa=file:label:[<control0>,<control1>,...]``
|
||||
Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin. This
|
||||
filter is reentrant, so multiple LADSPA plugins can be used at once.
|
||||
|
||||
``<file>``
|
||||
Specifies the LADSPA plugin library file.
|
||||
|
||||
.. note::
|
||||
|
||||
See also the note about the ``LADSPA_PATH`` variable in the
|
||||
`ENVIRONMENT VARIABLES`_ section.
|
||||
``<label>``
|
||||
Specifies the filter within the library. Some libraries contain only
|
||||
one filter, but others contain many of them. Entering 'help' here
|
||||
will list all available filters within the specified library, which
|
||||
eliminates the use of 'listplugins' from the LADSPA SDK.
|
||||
``[<control0>,<control1>,...]``
|
||||
Controls are zero or more ``,`` separated floating point values that
|
||||
determine the behavior of the loaded plugin (for example delay,
|
||||
threshold or gain).
|
||||
In verbose mode (add ``-v`` to the mpv command line), all
|
||||
available controls and their valid ranges are printed. This eliminates
|
||||
the use of 'analyseplugin' from the LADSPA SDK.
|
||||
Note that ``,`` is already used by the option parser to separate
|
||||
filters, so you must quote the list of values with ``[...]`` or
|
||||
similar.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=ladspa='/usr/lib/ladspa/delay.so':delay_5s:[0.5,0.2] media.avi``
|
||||
Does something.
|
||||
|
||||
``karaoke``
|
||||
Simple voice removal filter exploiting the fact that voice is usually
|
||||
recorded with mono gear and later 'center' mixed onto the final audio
|
||||
stream. Beware that this filter will turn your signal into mono. Works
|
||||
well for 2 channel tracks; do not bother trying it on anything but 2
|
||||
channel stereo.
|
||||
|
||||
``scaletempo[=option1:option2:...]``
|
||||
Scales audio tempo without altering pitch, optionally synced to playback
|
||||
speed (default).
|
||||
|
||||
+8
-6
@@ -1091,6 +1091,9 @@ Property list
|
||||
An example of these kind of metadata are the cropping parameters
|
||||
added by ``--vf=lavfi=cropdetect``.
|
||||
|
||||
``af-metadata/<filter-label>``
|
||||
Equivalent to ``vf-metadata/<filter-label>``, but for audio filters.
|
||||
|
||||
``pause`` (RW)
|
||||
Pause status. This is usually ``yes`` or ``no``. See ``--pause``.
|
||||
|
||||
@@ -1111,11 +1114,13 @@ Property list
|
||||
Network cache fill state (0-100.0).
|
||||
|
||||
``cache-size`` (RW)
|
||||
Total network cache size in KB. This is similar to ``--cache``. This allows
|
||||
Network cache size in KB. This is similar to ``--cache``. This allows
|
||||
to set the cache size at runtime. Currently, it's not possible to enable
|
||||
or disable the cache at runtime using this property, just to resize an
|
||||
existing cache.
|
||||
|
||||
This does not include the backbuffer size (changed after mpv 0.10.0).
|
||||
|
||||
Note that this tries to keep the cache contents as far as possible. To make
|
||||
this easier, the cache resizing code will allocate the new cache while the
|
||||
old cache is still allocated.
|
||||
@@ -1166,9 +1171,6 @@ Property list
|
||||
is loaded, or when switching ordered chapter segments. This is because
|
||||
the same underlying code is used for seeking and resyncing.)
|
||||
|
||||
``pts-association-mode`` (RW)
|
||||
See ``--pts-association-mode``.
|
||||
|
||||
``hr-seek`` (RW)
|
||||
See ``--hr-seek``.
|
||||
|
||||
@@ -1255,8 +1257,8 @@ Property list
|
||||
``colormatrix-input-range`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
``colormatrix-output-range`` (R)
|
||||
See ``colormatrix``.
|
||||
``video-output-levels`` (RW)
|
||||
See ``--video-output-levels``,
|
||||
|
||||
``colormatrix-primaries`` (R)
|
||||
See ``colormatrix``.
|
||||
|
||||
+71
-36
@@ -456,16 +456,13 @@ Program Behavior
|
||||
|
||||
If the script can't do anything with an URL, it will do nothing.
|
||||
|
||||
(Note: this is the replacement for the now removed libquvi support.)
|
||||
|
||||
``--ytdl-format=<best|worst|mp4|webm|...>``
|
||||
Video format/quality that is directly passed to youtube-dl. The possible
|
||||
values are specific to the website and the video, for a given url the
|
||||
available formats can be found with the command
|
||||
``youtube-dl --list-formats URL``. See youtube-dl's documentation for
|
||||
available aliases. To use experimental DASH support for youtube, use
|
||||
``bestvideo+bestaudio``.
|
||||
(Default: ``best``)
|
||||
available aliases.
|
||||
(Default: youtube-dl's default, currently ``bestvideo+bestaudio/best``)
|
||||
|
||||
``--ytdl-raw-options=<key>=<value>[,<key>=<value>[,...]]``
|
||||
Pass arbitrary options to youtube-dl. Parameter and argument should be
|
||||
@@ -567,12 +564,11 @@ Video
|
||||
:no: always use software decoding (default)
|
||||
:auto: see below
|
||||
:vdpau: requires ``--vo=vdpau`` or ``--vo=opengl`` (Linux only)
|
||||
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only)
|
||||
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux only)
|
||||
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
|
||||
:vda: requires ``--vo=opengl`` (OS X only)
|
||||
:videotoolbox: requires ``--vo=opengl`` (newer OS X only)
|
||||
:videotoolbox: requires ``--vo=opengl`` (OS X 10.8 and up only)
|
||||
:dxva2-copy: copies video back to system RAM (Windows only)
|
||||
:rpi: requires ``--vo=rpi`` (Raspberry Pi only - default if available)
|
||||
:rpi: requires ``--vo=rpi`` (Raspberry Pi only - default if available)
|
||||
|
||||
``auto`` tries to automatically enable hardware decoding using the first
|
||||
available method. This still depends what VO you are using. For example,
|
||||
@@ -581,6 +577,12 @@ Video
|
||||
work, it will always fall back to software decoding, instead of trying the
|
||||
next method (might matter on some Linux systems).
|
||||
|
||||
The ``vaapi`` mode, if used with ``--vo=opengl``, requires Mesa 11 and most
|
||||
likely works with Intel GPUs only. It also requires the opengl EGL backend
|
||||
(automatically used if available). You can also try the old GLX backend by
|
||||
forcing it with ``--vo=opengl:backend=x11``, but the vaapi/GLX interop is
|
||||
said to be slower than ``vaapi-copy``.
|
||||
|
||||
The ``vaapi-copy`` mode allows you to use vaapi with any VO. Because
|
||||
this copies the decoded video back to system RAM, it's likely less efficient
|
||||
than the ``vaapi`` mode.
|
||||
@@ -761,6 +763,10 @@ Video
|
||||
:top: top field first
|
||||
:bottom: bottom field first
|
||||
|
||||
.. note::
|
||||
|
||||
Setting either ``top`` or ``bottom`` will flag all frames as interlaced.
|
||||
|
||||
``--frames=<number>``
|
||||
Play/convert only first ``<number>`` video frames, then quit.
|
||||
|
||||
@@ -771,6 +777,26 @@ Video
|
||||
For audio-only playback, any value greater than 0 will quit playback
|
||||
immediately after initialization. The value 0 works as with video.
|
||||
|
||||
``--video-output-levels=<outputlevels>``
|
||||
RGB color levels used with YUV to RGB conversion. Normally, output devices
|
||||
such as PC monitors use full range color levels. However, some TVs and
|
||||
video monitors expect studio RGB levels. Providing full range output to a
|
||||
device expecting studio level input results in crushed blacks and whites,
|
||||
the reverse in dim gray blacks and dim whites.
|
||||
|
||||
Not all VOs support this option. Some will silently ignore it.
|
||||
|
||||
Available color ranges are:
|
||||
|
||||
:auto: automatic selection (equals to full range) (default)
|
||||
:limited: limited range (16-235 per component), studio levels
|
||||
:full: full range (0-255 per component), PC levels
|
||||
|
||||
.. note::
|
||||
|
||||
It is advisable to use your graphics driver's color range option
|
||||
instead, if available.
|
||||
|
||||
``--hwdec-codecs=<codec1,codec2,...|all>``
|
||||
Allow hardware decoding for a given list of codecs only. The special value
|
||||
``all`` always allows all codecs.
|
||||
@@ -797,6 +823,10 @@ Video
|
||||
The result is most likely broken decoding, but may also help if the
|
||||
detected or reported profiles are somehow incorrect.
|
||||
|
||||
``--vd-lavc-software-fallback=<yes|no>``
|
||||
Fallback to software decoding if the hardware-accelerated decoder fails
|
||||
(default: yes).
|
||||
|
||||
``--vd-lavc-bitexact``
|
||||
Only use bit-exact algorithms in all decoding steps (for codec testing).
|
||||
|
||||
@@ -888,6 +918,14 @@ Audio
|
||||
|
||||
Currently not implemented for most AOs.
|
||||
|
||||
``--audio-fallback-to-null=<yes|no>``
|
||||
If no audio device can be opened, behave as if ``--ao=null`` was given. This
|
||||
is useful in combination with ``--audio-device``: instead of causing an
|
||||
error if the selected device does not exist, the client API user (or a
|
||||
Lua script) could let playback continue normally, and check the
|
||||
``current-ao`` and ``audio-device-list`` properties to make high-level
|
||||
decisions about how to continue.
|
||||
|
||||
``--ao=<driver1[:suboption1[=value]:...],driver2,...[,]>``
|
||||
Specify a priority list of audio output drivers to be used. For
|
||||
interactive use one would normally specify a single one to use, but in
|
||||
@@ -2437,14 +2475,14 @@ OSD
|
||||
``--osd-duration=<time>``
|
||||
Set the duration of the OSD messages in ms (default: 1000).
|
||||
|
||||
``--osd-font=<pattern>``, ``--sub-text-font=<pattern>``
|
||||
``--osd-font=<name>``, ``--sub-text-font=<name>``
|
||||
Specify font to use for OSD and for subtitles that do not themselves
|
||||
specify a particular font. The default is ``sans-serif``.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
- ``--osd-font='Bitstream Vera Sans'``
|
||||
- ``--osd-font='Bitstream Vera Sans:style=Bold'`` (fontconfig pattern)
|
||||
- ``--osd-font='MS Comic Sans'``
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -2452,6 +2490,9 @@ OSD
|
||||
options) are ignored when ASS-subtitles are rendered, unless the
|
||||
``--no-sub-ass`` option is specified.
|
||||
|
||||
This used to support fontconfig patterns. Starting with libass 0.13.0,
|
||||
this stopped working.
|
||||
|
||||
``--osd-font-size=<size>``, ``--sub-text-font-size=<size>``
|
||||
Specify the OSD/sub font size. The unit is the size in scaled pixels at a
|
||||
window height of 720. The actual pixel size is scaled with the window
|
||||
@@ -2632,17 +2673,6 @@ OSD
|
||||
|
||||
Default: 0.
|
||||
|
||||
``--use-text-osd=<yes|no>``
|
||||
Disable text OSD rendering completely. (This includes the complete OSC as
|
||||
well.) This is mostly useful for avoiding loading fontconfig in situations
|
||||
where fontconfig does not behave well, and OSD is unused - this could for
|
||||
example allow GUI programs using libmpv to workaround fontconfig issues.
|
||||
|
||||
Note that selecting subtitles of any kind still initializes fontconfig.
|
||||
|
||||
Default: ``no``.
|
||||
|
||||
|
||||
Screenshot
|
||||
----------
|
||||
|
||||
@@ -3241,6 +3271,13 @@ Network
|
||||
Verify peer certificates when using TLS (e.g. with ``https://...``).
|
||||
(Silently fails with older FFmpeg or Libav versions.)
|
||||
|
||||
``--tls-cert-file``
|
||||
A file containing a certificate to use in the handshake with the
|
||||
peer.
|
||||
|
||||
``--tls-key-file``
|
||||
A file containing the private key for the certificate.
|
||||
|
||||
``--referrer=<string>``
|
||||
Specify a referrer path or URL for HTTP requests.
|
||||
|
||||
@@ -3426,6 +3463,11 @@ Miscellaneous
|
||||
:display-vdrop: Drop or repeat video frames to compensate desyncing
|
||||
video. (Although it should have the same effects as
|
||||
``audio``, the implementation is very different.)
|
||||
:display-adrop: Drop or repeat audio data to compensate desyncing
|
||||
video. See ``--video-sync-adrop-size``. This mode will
|
||||
cause severe audio artifacts if the real monitor
|
||||
refresh rate is too different from the reported or
|
||||
forced rate.
|
||||
:display-desync: Sync video to display, and let audio play on its own.
|
||||
:desync: Sync video according to system clock, and let audio play
|
||||
on its own.
|
||||
@@ -3457,6 +3499,13 @@ Miscellaneous
|
||||
frame dropping due to the audio "overshooting" and skipping multiple video
|
||||
frames before the sync logic can react.
|
||||
|
||||
``--video-sync-adrop-size=<value``
|
||||
For the ``--video-sync=display-adrop`` mode. This mode duplicates/drops
|
||||
audio data to keep audio in sync with video. To avoid audio artifacts on
|
||||
jitter (which would add/remove samples all the time), this is done in
|
||||
relatively large, fixed units, controlled by this option. The unit is
|
||||
seconds.
|
||||
|
||||
``--mf-fps=<value>``
|
||||
Framerate used when decoding from multiple PNG or JPEG files with ``mf://``
|
||||
(default: 1).
|
||||
@@ -3498,20 +3547,6 @@ Miscellaneous
|
||||
|
||||
.. warning:: Using realtime priority can cause system lockup.
|
||||
|
||||
``--pts-association-mode=<decode|sort|auto>``
|
||||
Select the method used to determine which container packet timestamp
|
||||
corresponds to a particular output frame from the video decoder. Normally
|
||||
you should not need to change this option.
|
||||
|
||||
:decoder: Use decoder reordering functionality. Unlike in classic MPlayer
|
||||
and mplayer2, this includes a DTS fallback. (Default.)
|
||||
:sort: Maintain a buffer of unused pts values and use the lowest value
|
||||
for the frame.
|
||||
:auto: Try to pick a working mode from the ones above automatically.
|
||||
|
||||
You can also try to use ``--no-correct-pts`` for files with completely
|
||||
broken timestamps.
|
||||
|
||||
``--force-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
|
||||
|
||||
+4
-88
@@ -17,6 +17,10 @@ normal filter parameters.
|
||||
|
||||
To get a full list of available video filters, see ``--vf=help``.
|
||||
|
||||
Also, keep in mind that most actual filters are available via the ``lavfi``
|
||||
wrapper, which gives you access to most of libavfilter's filters. This
|
||||
includes all filters that have been ported from MPlayer to libavfilter.
|
||||
|
||||
Video filters are managed in lists. There are a few commands to manage the
|
||||
filter list.
|
||||
|
||||
@@ -255,26 +259,6 @@ Available filters are:
|
||||
:limited: limited range (16-235 for luma, 16-240 for chroma)
|
||||
:full: full range (0-255 for both luma and chroma)
|
||||
|
||||
``<outputlevels>``
|
||||
RGB color levels used with YUV to RGB conversion. Normally, output devices
|
||||
such as PC monitors use full range color levels. However, some TVs and
|
||||
video monitors expect studio RGB levels. Providing full range output to a
|
||||
device expecting studio level input results in crushed blacks and whites,
|
||||
the reverse in dim gray blacks and dim whites.
|
||||
|
||||
The same limitations as with ``<colormatrix>`` apply.
|
||||
|
||||
Available color ranges are:
|
||||
|
||||
:auto: automatic selection (equals to full range) (default)
|
||||
:limited: limited range (16-235 per component), studio levels
|
||||
:full: full range (0-255 per component), PC levels
|
||||
|
||||
.. note::
|
||||
|
||||
It is advisable to use your graphics driver's color range option
|
||||
instead, if available.
|
||||
|
||||
``<primaries>``
|
||||
RGB primaries the source file was encoded with. Normally this should be set
|
||||
in the file header, but when playing broken or mistagged files this can be
|
||||
@@ -409,41 +393,6 @@ Available filters are:
|
||||
``'--vf=lavfi=yadif:o="threads=2,thread_type=slice"'``
|
||||
forces a specific threading configuration.
|
||||
|
||||
``noise[=<strength>[:averaged][:pattern][:temporal][:uniform][:hq]``
|
||||
Adds noise.
|
||||
|
||||
``strength``
|
||||
Set the noise for all components. If you want different strength
|
||||
values for luma and chroma, use libavfilter's noise filter directly
|
||||
(using ``--vf=lavfi=[noise=...]``), or tell the libavfilter developers
|
||||
to stop being stupid.
|
||||
|
||||
``averaged``
|
||||
averaged temporal noise (smoother, but a lot slower)
|
||||
|
||||
``pattern``
|
||||
mix random noise with a (semi)regular pattern
|
||||
|
||||
``temporal``
|
||||
temporal noise (noise pattern changes between frames)
|
||||
|
||||
``uniform``
|
||||
uniform noise (Gaussian otherwise)
|
||||
|
||||
``hqdn3d[=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp]``
|
||||
This filter aims to reduce image noise producing smooth images and making
|
||||
still images really still (This should enhance compressibility.).
|
||||
|
||||
``<luma_spatial>``
|
||||
spatial luma strength (default: 4)
|
||||
``<chroma_spatial>``
|
||||
spatial chroma strength (default: 3)
|
||||
``<luma_tmp>``
|
||||
luma temporal strength (default: 6)
|
||||
``<chroma_tmp>``
|
||||
chroma temporal strength (default:
|
||||
``luma_tmp*chroma_spatial/luma_spatial``)
|
||||
|
||||
``eq[=gamma:contrast:brightness:saturation:rg:gg:bg:weight]``
|
||||
Software equalizer that uses lookup tables (slow), allowing gamma correction
|
||||
in addition to simple brightness and contrast adjustment. The parameters are
|
||||
@@ -470,23 +419,6 @@ Available filters are:
|
||||
and just plain white. A value of 0.0 turns the gamma correction all
|
||||
the way down while 1.0 leaves it at its full strength (default: 1.0).
|
||||
|
||||
``unsharp[=lx:ly:la:cx:cy:ca]``
|
||||
unsharp mask / Gaussian blur
|
||||
|
||||
``l`` is for the luma component, ``c`` for the chroma component. ``x``/``y``
|
||||
is the filter size. ``a`` is the amount.
|
||||
|
||||
``lx``, ``ly``, ``cx``, ``cy``
|
||||
width and height of the matrix, odd sized in both directions (min =
|
||||
3:3, max = 13:11 or 11:13, usually something between 3:3 and 7:7)
|
||||
|
||||
``la``, ``ca``
|
||||
Relative amount of sharpness/blur to add to the image (a sane range
|
||||
should be -1.5-1.5).
|
||||
|
||||
:<0: blur
|
||||
:>0: sharpen
|
||||
|
||||
``pullup[=jl:jr:jt:jb:sb:mp]``
|
||||
Pulldown reversal (inverse telecine) filter, capable of handling mixed
|
||||
hard-telecine, 24000/1001 fps progressive, and 30000/1001 fps progressive
|
||||
@@ -545,22 +477,6 @@ Available filters are:
|
||||
when inserting yadif with ``--vf``, so using the above methods is
|
||||
recommended.
|
||||
|
||||
``delogo[=x:y:w:h:t:show]``
|
||||
Suppresses a TV station logo by a simple interpolation of the surrounding
|
||||
pixels. Just set a rectangle covering the logo and watch it disappear (and
|
||||
sometimes something even uglier appear - your mileage may vary).
|
||||
|
||||
``<x>,<y>``
|
||||
top left corner of the logo
|
||||
``<w>,<h>``
|
||||
width and height of the cleared rectangle
|
||||
``<t>``
|
||||
Thickness of the fuzzy edge of the rectangle (added to ``w`` and
|
||||
``h``). When set to -1, a green rectangle is drawn on the screen to
|
||||
simplify finding the right ``x``,``y``,``w``,``h`` parameters.
|
||||
``show``
|
||||
Draw a rectangle showing the area defined by x/y/w/h.
|
||||
|
||||
``sub=[=bottom-margin:top-margin]``
|
||||
Moves subtitle rendering to an arbitrary point in the filter chain, or force
|
||||
subtitle rendering in the video filter as opposed to using video output OSD
|
||||
|
||||
+76
-36
@@ -20,13 +20,10 @@ normal driver parameters.
|
||||
|
||||
See ``--vo=help`` for a list of compiled-in video output drivers.
|
||||
|
||||
The recommended output drivers are ``--vo=vdpau`` and ``--vo=opengl-hq``.
|
||||
All other drivers are just for compatibility or special purposes.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``--vo=opengl,xv,``
|
||||
Try the ``opengl`` driver, then the ``xv`` driver, then others.
|
||||
The recommended output driver is ``--vo=opengl-hq``. All other drivers are
|
||||
for compatibility or special purposes. By default, ``--vo=opengl`` is used,
|
||||
but if that appears not to work, it fallback to other drivers (in the same
|
||||
order as listed by ``--vo=help``).
|
||||
|
||||
Available video output drivers are:
|
||||
|
||||
@@ -75,6 +72,12 @@ Available video output drivers are:
|
||||
not responding quickly enough if video FPS is close to or higher than
|
||||
the display refresh rate.
|
||||
|
||||
``x11`` (X11 only)
|
||||
Shared memory video output driver without hardware acceleration that works
|
||||
whenever X11 is present.
|
||||
|
||||
.. note:: This is a fallback only, and should not be normally used.
|
||||
|
||||
``vdpau`` (X11 only)
|
||||
Uses the VDPAU interface to display and optionally also decode video.
|
||||
Hardware decoding is used with ``--hwdec=vdpau``.
|
||||
@@ -290,6 +293,26 @@ Available video output drivers are:
|
||||
color space conversion and chroma upsampling is generally in the hand of
|
||||
the hardware decoder APIs.
|
||||
|
||||
``opengl`` makes use of FBOs by default. Sometimes you can achieve better
|
||||
quality or performance by changing the ``fbo-format`` suboption to
|
||||
``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include Mesa/Intel not
|
||||
accepting ``rgb16``, Mesa sometimes not being compiled with float texture
|
||||
support, and some OS X setups being very slow with ``rgb16`` but fast
|
||||
with ``rgb32f``. If you have problems, you can also try passing the
|
||||
``dumb-mode=yes`` sub-option.
|
||||
|
||||
``dumb-mode=<yes|no>``
|
||||
This mode is extremely restricted, and will disable most extended
|
||||
OpenGL features. This includes high quality scalers and custom
|
||||
shaders!
|
||||
|
||||
It is intended for hardware that does not support FBOs (including GLES,
|
||||
which supports it insufficiently), or to get some more performance out
|
||||
of bad or old hardware.
|
||||
|
||||
This mode is forced automatically if needed, and this option is mostly
|
||||
useful for debugging.
|
||||
|
||||
``scale=<filter>``
|
||||
|
||||
``bilinear``
|
||||
@@ -355,10 +378,6 @@ Available video output drivers are:
|
||||
Scale parameter (``t``). Increasing this makes the result blurrier.
|
||||
Defaults to 1.
|
||||
|
||||
sharpen3, sharpen5
|
||||
Sharpening strength. Increasing this makes the image sharper but
|
||||
adds more ringing and aliasing. Defaults to 0.5.
|
||||
|
||||
oversample
|
||||
Minimum distance to an edge before interpolation is used. Setting
|
||||
this to 0 will always interpolate edges, whereas setting it to 0.5
|
||||
@@ -388,8 +407,8 @@ Available video output drivers are:
|
||||
between 0.0 and 1.0. The default value of 0.0 disables antiringing
|
||||
entirely.
|
||||
|
||||
Note that this doesn't affect the special filters ``bilinear``,
|
||||
``bicubic_fast`` or ``sharpen``.
|
||||
Note that this doesn't affect the special filters ``bilinear`` and
|
||||
``bicubic_fast``.
|
||||
|
||||
``scale-window=<window>``
|
||||
(Advanced users only) Choose a custom windowing function for the kernel.
|
||||
@@ -530,17 +549,9 @@ Available video output drivers are:
|
||||
feature doesn't work correctly with different scale factors in
|
||||
different directions.
|
||||
|
||||
``source-shader=<file>``, ``scale-shader=<file>``, ``pre-shaders=<files>``, ``post-shaders=<files>``
|
||||
``pre-shaders=<files>``, ``post-shaders=<files>``, ``scale-shader=<file>``
|
||||
Custom GLSL fragment shaders.
|
||||
|
||||
source-shader
|
||||
This gets applied directly onto the source planes, before
|
||||
any sort of upscaling or conversion whatsoever. For YCbCr content,
|
||||
this means it gets applied on the luma and chroma planes
|
||||
separately. In general, this shader shouldn't be making any
|
||||
assumptions about the colorspace. It could be RGB, YCbCr, XYZ or
|
||||
something else entirely. It's used purely for fixing numerical
|
||||
quirks of the input, eg. debanding or deblocking.
|
||||
pre-shaders (list)
|
||||
These get applied after conversion to RGB and before linearization
|
||||
and upscaling. Operates on non-linear RGB (same as input). This is
|
||||
@@ -581,10 +592,6 @@ Available video output drivers are:
|
||||
never resets (regardless of seeks).
|
||||
vec2 image_size
|
||||
The size in pixels of the input image.
|
||||
float cmul (source-shader only)
|
||||
The multiplier needed to pull colors up to the right bit depth. The
|
||||
source-shader must multiply any sampled colors by this, in order
|
||||
to normalize them to the full scale.
|
||||
|
||||
For example, a shader that inverts the colors could look like this::
|
||||
|
||||
@@ -594,6 +601,37 @@ Available video output drivers are:
|
||||
return vec4(1.0 - color.rgb, color.a);
|
||||
}
|
||||
|
||||
``deband``
|
||||
Enable the debanding algorithm. This greatly reduces the amount of
|
||||
visible banding, blocking and other quantization artifacts, at the
|
||||
expensive of very slightly blurring some of the finest details. In
|
||||
practice, it's virtually always an improvement - the only reason to
|
||||
disable it would be for performance.
|
||||
|
||||
``deband-iterations=<1..16>``
|
||||
The number of debanding steps to perform per sample. Each step reduces
|
||||
a bit more banding, but takes time to compute. Note that the strength
|
||||
of each step falls off very quickly, so high numbers (>4) are
|
||||
practically useless. (Default 1)
|
||||
|
||||
``deband-threshold=<0..4096>``
|
||||
The debanding filter's cut-off threshold. Higher numbers increase the
|
||||
debanding strength dramatically but progressively diminish image
|
||||
details. (Default 64)
|
||||
|
||||
``deband-range=<1..64>``
|
||||
The debanding filter's initial radius. The radius increases linearly
|
||||
for each iteration. A higher radius will find more gradients, but
|
||||
a lower radius will smooth more aggressively. (Default 16)
|
||||
|
||||
If you increase the ``deband-iterations``, you should probably
|
||||
decrease this to compensate.
|
||||
|
||||
``deband-grain=<0..4096>``
|
||||
Add some extra noise to the image. This significantly helps cover up
|
||||
remaining quantization artifacts. Higher numbers add more noise.
|
||||
(Default 48)
|
||||
|
||||
``sigmoid-upscaling``
|
||||
When upscaling, use a sigmoidal color transform to avoid emphasizing
|
||||
ringing artifacts. This also implies ``linear-scaling``.
|
||||
@@ -606,6 +644,16 @@ Available video output drivers are:
|
||||
The slope of the sigmoid curve used for ``sigmoid-upscaling``, must
|
||||
be a float between 1.0 and 20.0. Defaults to 6.5 if not specified.
|
||||
|
||||
``sharpen=<value>``
|
||||
If set to a value other than 0, enable an unsharp masking filter.
|
||||
Positive values will sharpen the image (but add more ringing and
|
||||
aliasing). Negative values will blur the image. If your GPU is powerful
|
||||
enough, consider alternatives like the ``ewa_lanczossharp`` scale
|
||||
filter, or the ``scale-blur`` sub-option.
|
||||
|
||||
(This feature is the replacement for the old ``sharpen3`` and
|
||||
``sharpen5`` scalers.)
|
||||
|
||||
``glfinish``
|
||||
Call ``glFinish()`` before and after swapping buffers (default: disabled).
|
||||
Slower, but might help getting better results when doing framedropping.
|
||||
@@ -657,8 +705,7 @@ Available video output drivers are:
|
||||
|
||||
``fbo-format=<fmt>``
|
||||
Selects the internal format of textures used for FBOs. The format can
|
||||
influence performance and quality of the video output. (FBOs are not
|
||||
always used, and typically only when using extended scalers.)
|
||||
influence performance and quality of the video output.
|
||||
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb10_a2, rgb16, rgb16f,
|
||||
rgb32f, rgba12, rgba16, rgba16f, rgba32f.
|
||||
Default: rgba16.
|
||||
@@ -821,18 +868,11 @@ Available video output drivers are:
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
--vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:fancy-downscaling:sigmoid-upscaling:pbo
|
||||
--vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:fancy-downscaling:sigmoid-upscaling:pbo:deband
|
||||
|
||||
Note that some cheaper LCDs do dithering that gravely interferes with
|
||||
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
|
||||
|
||||
Unlike ``opengl``, ``opengl-hq`` makes use of FBOs by default. Sometimes you
|
||||
can achieve better quality or performance by changing the ``fbo-format``
|
||||
suboption to ``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include
|
||||
Mesa/Intel not accepting ``rgb16``, Mesa sometimes not being compiled with
|
||||
float texture support, and some OS X setups being very slow with ``rgb16``
|
||||
but fast with ``rgb32f``.
|
||||
|
||||
``sdl``
|
||||
SDL 2.0+ Render video output driver, depending on system with or without
|
||||
hardware acceleration. Should work on all platforms supported by SDL 2.0.
|
||||
|
||||
+69
-27
@@ -1,64 +1,106 @@
|
||||
Release Policy
|
||||
==============
|
||||
|
||||
Every few months, a new release is cut off of the master branch and is assigned
|
||||
a 0.X.0 version number.
|
||||
Once or twice a month, a new release is cut off of the master branch and is
|
||||
assigned a 0.X.Y version number, where X is incremented each time a release
|
||||
contains breaking changes, such as changed options or added/removed features,
|
||||
and Y is incremented if a release contains only bugfixes and other minor
|
||||
changes.
|
||||
|
||||
As part of the maintenance process, minor releases are made, which are assigned
|
||||
0.X.Y version numbers. Minor releases contain bug fixes only. They never merge
|
||||
the master branch, and no features are added to it. Only the latest release is
|
||||
maintained.
|
||||
There is only one release branch that keeps track of the latest version and
|
||||
will not be maintained separately.
|
||||
|
||||
The goal of releases is to provide stability and an unchanged base for the sake
|
||||
of Linux distributions. If you want the newest features, just use the master
|
||||
branch, which is stable most of the time, except sometimes, when it's not.
|
||||
The goal of releases is to provide Linux distributions with something to
|
||||
package. If you want the newest features, just use the master branch.
|
||||
We try our best to keep it deployable at all times.
|
||||
|
||||
Releases other than the latest release are unsupported and unmaintained.
|
||||
|
||||
Release procedure
|
||||
-----------------
|
||||
|
||||
- Create branch release/0.X or cherry-pick commits to the relevant branch.
|
||||
- Merge master into branch release/current.
|
||||
|
||||
- Create and/or update the ``RELEASE_NOTES`` file.
|
||||
- Create and/or update the `RELEASE_NOTES` file.
|
||||
|
||||
- Create and/or update the ``VERSION`` file.
|
||||
- Create and/or update the `VERSION` file.
|
||||
|
||||
- Update ``DOCS/client-api-changes.rst`` (on major releases).
|
||||
- Update `DOCS/client-api-changes.rst` (on major releases).
|
||||
|
||||
- Create tag v0.X.Y.
|
||||
|
||||
- Push branch and tag to GitHub.
|
||||
|
||||
- Create a new GitHub release using the content of ``RELEASE_NOTES`` related to
|
||||
- Create a new GitHub release using the content of `RELEASE_NOTES` related to
|
||||
the new version.
|
||||
|
||||
Release notes template
|
||||
----------------------
|
||||
|
||||
Here is a template that can be used for writing the ``RELEASE_NOTES`` file.
|
||||
Here is a template that can be used for writing the `RELEASE_NOTES` file.
|
||||
|
||||
```markdown
|
||||
Release 0.X.Y
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
Features
|
||||
--------
|
||||
|
||||
- List of changes.
|
||||
New
|
||||
~~~
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
- List of new features
|
||||
|
||||
- List of bug fixes.
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
New features
|
||||
------------
|
||||
- List of removed features
|
||||
|
||||
- List of new features.
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- List of deprecated features
|
||||
|
||||
Behavior
|
||||
--------
|
||||
|
||||
- List of user-visible changes in behavior
|
||||
|
||||
Options and Commands
|
||||
--------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- List of added options and commands
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- List of changed options and commands
|
||||
|
||||
Renamed
|
||||
~~~~~~~
|
||||
|
||||
- List of renamed options and commands
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- List of deprecated options and commands
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- List of removed options and commands
|
||||
|
||||
Fixes and Minor Enhancements
|
||||
----------------------------
|
||||
|
||||
- List of fixes and minor enhancements
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log <start>..<end>`` in
|
||||
complete change log can be viewed by running `git log <start>..<end>` in
|
||||
the git repository.
|
||||
```
|
||||
|
||||
@@ -66,6 +108,6 @@ Note that the "Release 0.X.Y" title should be removed when creating a new GitHub
|
||||
release.
|
||||
|
||||
When creating a new point release its changes should be added on top of the
|
||||
``RELEASE_NOTES`` file (with the appropriate title) so that all the changes in
|
||||
the current 0.X branch will be included. This way the ``RELEASE_NOTES`` file
|
||||
`RELEASE_NOTES` file (with the appropriate title) so that all the changes in
|
||||
the current 0.X branch will be included. This way the `RELEASE_NOTES` file
|
||||
can be used by distributors as changelog for point releases too.
|
||||
@@ -187,7 +187,7 @@ only if discretion is required.
|
||||
[windows_compilation]: https://github.com/mpv-player/mpv/blob/master/DOCS/compile-windows.md
|
||||
[mplayer-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/mplayer-changes.rst
|
||||
|
||||
## License
|
||||
## License
|
||||
|
||||
|
||||
[GPLv2](https://github.com/mpv-player/mpv/blob/master/LICENSE)
|
||||
Mostly GPLv2 or later. See [details.](https://github.com/mpv-player/mpv/blob/master/Copyright)
|
||||
+167
@@ -1,3 +1,170 @@
|
||||
Release 0.12.0
|
||||
==============
|
||||
|
||||
NOTE: This release changes the default format for youtube-dl and removes some
|
||||
workarounds related to MPEG DASH support. However, the required changes to
|
||||
FFmpeg code are not in any FFmpeg release yet (as of 2015-10-29), so for now,
|
||||
playing DASH streams requires using FFmpeg git master. You can work around
|
||||
this by using the --ytdl-format=best option.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
New
|
||||
~~~
|
||||
|
||||
- vo_opengl: support new VAAPI EGL interop (requires Mesa 11)
|
||||
- vo_opengl: vaapi: add Wayland support
|
||||
- bring back the x11 video output
|
||||
- vo_opengl: support all kinds of GBRP formats
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- video: remove VDA support (VideoToolbox is preferred)
|
||||
|
||||
Behavior
|
||||
--------
|
||||
|
||||
- vo_opengl: enable X11 EGL backend by default (disabled for NVIDIA due to driver bugs)
|
||||
- ytdl: don't override user-set format in no-video mode
|
||||
- sub: adjust behavior on mismatching video/subtitle aspect ratio
|
||||
|
||||
|
||||
Options and Commands
|
||||
--------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- audio: add option for falling back to ao_null
|
||||
- options: add support for client certificate authentication
|
||||
- input: add key name for U+3000 IDEOGRAPHIC SPACE
|
||||
- player: add audio drop/duplicate mode to video-sync
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- vo_opengl: remove sharpen scalers, add sharpen sub-option
|
||||
- vo_opengl: make sw suboption work without explicit backend selection
|
||||
- command: make time properties unavailable if timestamp is unknown
|
||||
- command: do not return 0 for bitrate if unknown (make property unavailable instead)
|
||||
- vo_opengl: make the default debanding settings less excessive
|
||||
- ytdl: disable --all-subs if "sub-lang" is in raw-options
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- video: remove user-controllable PTS sorting (--pts-association-mode)
|
||||
|
||||
|
||||
Fixes and Minor Enhancements
|
||||
----------------------------
|
||||
|
||||
- player: fix another --force-window bug
|
||||
- player: add wav to list of external audio file extensions
|
||||
- ao_alsa: fix failure to find any sample format
|
||||
- player: make stop command actually stop in all cases
|
||||
- audio: various fixes related to audio device hotplugging
|
||||
- vo_xv: fix crash with --wid
|
||||
- ytdl: Remove DASH hacks, use DASH by default
|
||||
- player: be slightly less prone to framedrop in display sync mode
|
||||
- sd_lavc: extend subtitle resolution if images go outside video frame
|
||||
- player: offset chapter display by start time
|
||||
- command: make bitrate properties work correctly for external tracks
|
||||
- w32_common: disable IME
|
||||
- player: fix display-sync A/V calculation on high playback speeds
|
||||
- player: fix display sync A/V difference estimation on drops
|
||||
- player: raise display sync desync tolerance
|
||||
|
||||
|
||||
This listing is not complete. Check DOCS/client-api-changes.rst for a history
|
||||
of changes to the client API. A complete changelog can be seen by running
|
||||
`git log v0.11.0..v0.12.0` in the git repository or by visiting either
|
||||
https://github.com/mpv-player/mpv/compare/v0.11.0...v0.12.0 or
|
||||
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.11.0..v0.12.0
|
||||
|
||||
|
||||
Release 0.11.0
|
||||
==============
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
New
|
||||
~~~
|
||||
|
||||
- vo_opengl: implement debanding
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- audio/filter: remove center, extrastereo, karaoke, sinesuppress, sub,
|
||||
surround, sweep, ladspa, hrtf, export and bs2b filters (these are either
|
||||
considered useless or have replacements in lavfi)
|
||||
- video/filter: remove lavfi wrappers for noise, hqdn3d, unsharp and delogo
|
||||
(these filters remain usable through lavfi)
|
||||
|
||||
Behavior
|
||||
--------
|
||||
|
||||
- vo_opengl: require FBOs by default (use dumb-mode suboption for old hardware
|
||||
and broken drivers)
|
||||
- vo_opengl: enable debanding by default for the opengl-hq preset
|
||||
- audio/out: use new sample format determination code
|
||||
- player: prefer logical current directory path (affects logic for resuming
|
||||
playback)
|
||||
- vf_vdpaurb: Pass through non-hardware-decoded content
|
||||
- player: make force-window=immediate work in auto-profiles
|
||||
|
||||
|
||||
Options and Commands
|
||||
--------------------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- af_lavrresample: add normalize suboption
|
||||
- vo_opengl: add deband, deband-iterations, deband-threshold, deband-range and
|
||||
deband-grain suboptions
|
||||
- af_lavfi: implement af-metadata property (like vf-metadata)
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- command: make "add <property> 0" not change the value
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- vo_opengl: remove source-shader suboption
|
||||
|
||||
|
||||
Fixes and Minor Enhancements
|
||||
----------------------------
|
||||
|
||||
- options: fix --no-config
|
||||
- cache: do not include backbuffer size in total stream cache size
|
||||
- audio/format: actually prefer float over double sample format for
|
||||
int->float conversions
|
||||
- audio/format: fix interlaved vs. non-interleaved conversions
|
||||
- audio/format: revise format conversion scoring
|
||||
- video: make --field-dominance set interlaced flag
|
||||
- vf: vf_stereo3d compilation depends on libavfilter
|
||||
- vf_yadif: add hack for Libav compatibility
|
||||
- player: add opus to list of external audio file extensions
|
||||
- build: allow disabling vapoursynth completely
|
||||
- libmpv/win32: allow multiple windows at the same time
|
||||
|
||||
|
||||
This listing is not complete. Check DOCS/client-api-changes.rst for a history
|
||||
of changes to the client API. A complete changelog can be seen by running
|
||||
`git log v0.10.0..v0.11.0` in the git repository or by visiting either
|
||||
https://github.com/mpv-player/mpv/compare/v0.10.0...v0.11.0 or
|
||||
http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.10.0..v0.11.0
|
||||
|
||||
|
||||
Release 0.10.0
|
||||
==============
|
||||
|
||||
|
||||
+6
-6
@@ -95,18 +95,18 @@ case "$verdict" in
|
||||
exit 0
|
||||
;;
|
||||
interlaced-tff)
|
||||
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=top
|
||||
judge "$@" --vf-clr --vf-pre=lavfi=\[setfield=tff\],pullup
|
||||
case "$verdict" in
|
||||
progressive)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=top
|
||||
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=tff\],pullup
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=top
|
||||
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=tff\],yadif
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
@@ -114,18 +114,18 @@ case "$verdict" in
|
||||
esac
|
||||
;;
|
||||
interlaced-bff)
|
||||
judge "$@" --vf-clr --vf-pre=pullup --field-dominance=bottom
|
||||
judge "$@" --vf-clr --vf-pre=lavfi=\[setfield=bff\],pullup
|
||||
case "$verdict" in
|
||||
progressive)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=bottom
|
||||
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=bff\],pullup
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=bottom
|
||||
$ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=bff\],yadif
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
|
||||
@@ -30,7 +30,12 @@ function add_files_at(index, files)
|
||||
end
|
||||
|
||||
function get_extension(path)
|
||||
return string.match(path, "%.([^%.]+)$" )
|
||||
match = string.match(path, "%.([^%.]+)$" )
|
||||
if match == nil then
|
||||
return "nomatch"
|
||||
else
|
||||
return match
|
||||
end
|
||||
end
|
||||
|
||||
table.filter = function(t, iter)
|
||||
|
||||
+29
-15
@@ -191,8 +191,6 @@ options_state_machine() {
|
||||
opt_yes_no _libcdio "libcdio support"
|
||||
opt_yes_no _librubberband "librubberband support"
|
||||
opt_yes_no _ffmpeg "skip FFmpeg/Libav autodetection"
|
||||
opt_yes_no _ladspa "LADSPA plugin support"
|
||||
opt_yes_no _libbs2b "libbs2b audio filter support"
|
||||
opt_yes_no _libavresample "libavresample (preferred over libswresample)"
|
||||
opt_yes_no _libswresample "libswresample"
|
||||
opt_yes_no _caca "CACA video output"
|
||||
@@ -612,12 +610,13 @@ define_yes_no $_vdpau HAVE_VDPAU_HWACCEL
|
||||
check_pkg_config "VAAPI" $_vaapi VAAPI 'libva >= 0.32.0 libva-x11 >= 0.32.0'
|
||||
_vaapi=$(defretval)
|
||||
define_yes_no $_vaapi HAVE_VAAPI_HWACCEL
|
||||
define_yes_no $_vaapi HAVE_VAAPI_X11
|
||||
|
||||
_vaapi_wayland=no
|
||||
if test "$_vaapi" = yes ; then
|
||||
check_pkg_config "VAAPI VPP" auto VAAPI_VPP 'libva >= 0.34.0'
|
||||
else
|
||||
check_yes_no no VAAPI_VPP
|
||||
_vaapi_wayland=auto
|
||||
fi
|
||||
check_pkg_config "VAAPI Wayland" $_vaapi_wayland VAAPI_WAYLAND 'libva-wayland >= 0.34.0'
|
||||
|
||||
check_pkg_config "Xinerama" $_xinerama XINERAMA 'xinerama'
|
||||
|
||||
@@ -705,6 +704,11 @@ _vaapi_glx=no
|
||||
(test "$_gl_x11" = yes && test "$_vaapi" = yes) && _vaapi_glx=auto
|
||||
check_pkg_config "VAAPI with OpenGL/X11" $_vaapi_glx VAAPI_GLX 'libva-glx >= 0.32.0'
|
||||
|
||||
_vaapi_x_egl=no
|
||||
(test "$_gl_x11_egl" = yes && test "$_vaapi" = yes) && _vaapi_x_egl=yes
|
||||
check_yes_no $_vaapi_x_egl VAAPI_X_EGL
|
||||
check_yes_no $_vaapi_x_egl VAAPI_EGL
|
||||
|
||||
check_pkg_config "SDL 2.0" $_sdl2 SDL2 'sdl2'
|
||||
|
||||
check_statement_libs "OSS Audio" $_ossaudio OSS_AUDIO $_soundcard_header "int x = SNDCTL_DSP_SETFRAGMENT;"
|
||||
@@ -749,11 +753,6 @@ check_pkg_config "uchardet" $_uchardet UCHARDET 'uchardet'
|
||||
check_pkg_config "zlib" auto ZLIB 'zlib'
|
||||
test $(defretval) = no && die "Unable to find development files for zlib."
|
||||
|
||||
test "$_dl" = no && _ladspa=no
|
||||
check_statement_libs "LADSPA plugin support" $_ladspa LADSPA ladspa.h 'LADSPA_Descriptor ld = {0}'
|
||||
|
||||
check_pkg_config "libbs2b audio filter support" $_libbs2b LIBBS2B 'libbs2b'
|
||||
|
||||
check_pkg_config "LCMS2 support" $_lcms2 LCMS2 'lcms2 >= 2.6'
|
||||
|
||||
check_pkg_config "FFmpeg/Libav" $_ffmpeg FFMPEG \
|
||||
@@ -805,6 +804,24 @@ api_statement_check \
|
||||
libavutil/avutil.h \
|
||||
'const char *x = av_version_info()'
|
||||
|
||||
api_statement_check \
|
||||
"libavutil new pixdesc fields" \
|
||||
HAVE_AV_NEW_PIXDESC \
|
||||
libavutil/pixdesc.h \
|
||||
'AVComponentDescriptor d; int x = d.depth'
|
||||
|
||||
api_statement_check \
|
||||
"libavcodec 64 bit AVPacket.duration" \
|
||||
HAVE_AV_AVPACKET_INT64_DURATION \
|
||||
libavcodec/avcodec.h \
|
||||
'int x[(int)sizeof(((AVPacket){0}).duration) - 7]'
|
||||
|
||||
api_statement_check \
|
||||
"libavcodec AVSubtitleRect AVPicture removal" \
|
||||
HAVE_AV_SUBTITLE_NOPICT \
|
||||
libavcodec/avcodec.h \
|
||||
'AVSubtitleRect r = {.linesize={0}}'
|
||||
|
||||
check_pkg_config "libavfilter" $_libavfilter LIBAVFILTER 'libavfilter >= 5.0.0'
|
||||
|
||||
check_pkg_config "libavdevice" $_libavdevice LIBAVDEVICE 'libavdevice >= 55.0.0'
|
||||
@@ -927,10 +944,6 @@ cat > $TMPC << EOF
|
||||
for platforms not supported by this configure script */
|
||||
#define HAVE_BSD_FSTATFS 0
|
||||
#define HAVE_LINUX_FSTATFS 1
|
||||
#define HAVE_VDA_AV_VDA_ALLOC_CONTEXT 0
|
||||
#define HAVE_VDA_HWACCEL 0
|
||||
#define HAVE_VDA_LIBAVCODEC_REFCOUNTING 0
|
||||
#define HAVE_VDA_GL 0
|
||||
#define HAVE_GL_COCOA 0
|
||||
#define HAVE_COCOA 0
|
||||
#define HAVE_COCOA_APPLICATION 0
|
||||
@@ -959,7 +972,8 @@ cat > $TMPC << EOF
|
||||
#define HAVE_DRM 0
|
||||
#define HAVE_VIDEOTOOLBOX_HWACCEL 0
|
||||
#define HAVE_VIDEOTOOLBOX_GL 0
|
||||
#define HAVE_VIDEOTOOLBOX_VDA_GL 0
|
||||
#define HAVE_SSE4_INTRINSICS 1
|
||||
#define HAVE_C11_TLS 1
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define DEFAULT_CDROM_DEVICE "/dev/rcd0c"
|
||||
|
||||
+20
-29
@@ -35,13 +35,11 @@ SOURCES-$(DVDREAD) += stream/stream_dvd.c \
|
||||
SOURCES-$(DVDNAV) += stream/stream_dvdnav.c \
|
||||
stream/stream_dvd_common.c
|
||||
|
||||
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
|
||||
SOURCES-$(RUBBERBAND) += audio/filter/af_rubberband.c
|
||||
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c \
|
||||
demux/demux_libass.c
|
||||
|
||||
SOURCES-$(LIBBLURAY) += stream/stream_bluray.c
|
||||
SOURCES-$(LIBBS2B) += audio/filter/af_bs2b.c
|
||||
|
||||
SOURCES-$(LIBSMBCLIENT) += stream/stream_smb.c
|
||||
|
||||
@@ -58,19 +56,24 @@ SOURCES-$(LIBASS_OSD) += sub/osd_libass.c
|
||||
SOURCES-$(ALSA) += audio/out/ao_alsa.c
|
||||
SOURCES-$(CACA) += video/out/vo_caca.c
|
||||
SOURCES-$(SDL2) += audio/out/ao_sdl.c video/out/vo_sdl.c
|
||||
SOURCES-$(GL) += video/out/gl_common.c video/out/gl_osd.c \
|
||||
video/out/vo_opengl.c video/out/gl_lcms.c \
|
||||
video/out/gl_video.c video/out/dither.c \
|
||||
video/out/gl_hwdec.c video/out/gl_utils.c \
|
||||
SOURCES-$(GL) += video/out/opengl/common.c \
|
||||
video/out/opengl/osd.c \
|
||||
video/out/opengl/lcms.c \
|
||||
video/out/opengl/video.c \
|
||||
video/out/opengl/video_shaders.c \
|
||||
video/out/dither.c \
|
||||
video/out/opengl/hwdec.c \
|
||||
video/out/opengl/utils.c \
|
||||
video/out/vo_opengl.c \
|
||||
video/out/vo_opengl_cb.c
|
||||
|
||||
SOURCES-$(ENCODING) += video/out/vo_lavc.c audio/out/ao_lavc.c \
|
||||
common/encode_lavc.c
|
||||
|
||||
SOURCES-$(GL_X11) += video/out/x11_common.c video/out/gl_x11.c
|
||||
SOURCES-$(EGL_X11) += video/out/x11_common.c video/out/gl_x11egl.c
|
||||
SOURCES-$(GL_X11) += video/out/x11_common.c video/out/opengl/x11.c
|
||||
SOURCES-$(EGL_X11) += video/out/x11_common.c video/out/opengl/x11egl.c
|
||||
SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
|
||||
video/out/gl_wayland.c
|
||||
video/out/opengl/wayland.c
|
||||
|
||||
SOURCES-$(JACK) += audio/out/ao_jack.c
|
||||
SOURCES-$(OPENAL) += audio/out/ao_openal.c
|
||||
@@ -82,27 +85,25 @@ SOURCES-$(VDPAU) += video/vdpau.c video/vdpau_mixer.c \
|
||||
video/out/vo_vdpau.c video/decode/vdpau.c \
|
||||
video/filter/vf_vdpaupp.c \
|
||||
video/filter/vf_vdpaurb.c
|
||||
SOURCES-$(VDPAU_GL_X11) += video/out/gl_hwdec_vdpau.c
|
||||
SOURCES-$(VDPAU_GL_X11) += video/out/opengl/hwdec_vdpau.c
|
||||
SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
|
||||
video/decode/vaapi.c \
|
||||
video/filter/vf_vavpp.c \
|
||||
video/vaapi.c
|
||||
SOURCES-$(VAAPI_VPP) += video/filter/vf_vavpp.c
|
||||
SOURCES-$(VAAPI_GLX) += video/out/gl_hwdec_vaglx.c
|
||||
SOURCES-$(VAAPI_GLX) += video/out/opengl/hwdec_vaglx.c
|
||||
SOURCES-$(VAAPI_X_EGL) += video/out/opengl/hwdec_vaegl.c
|
||||
|
||||
SOURCES-$(XV) += video/out/x11_common.c video/out/vo_xv.c
|
||||
SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
|
||||
SOURCES-$(XV) += video/out/vo_xv.c
|
||||
SOURCES-$(WAYLAND) += video/out/vo_wayland.c \
|
||||
video/out/wayland_common.c \
|
||||
video/out/wayland/buffer.c \
|
||||
video/out/wayland/memfile.c
|
||||
|
||||
SOURCES-$(LIBAVFILTER) += video/filter/vf_lavfi.c \
|
||||
video/filter/vf_delogo.c \
|
||||
video/filter/vf_gradfun.c \
|
||||
video/filter/vf_hqdn3d.c \
|
||||
video/filter/vf_noise.c \
|
||||
video/filter/vf_pullup.c \
|
||||
video/filter/vf_rotate.c \
|
||||
video/filter/vf_unsharp.c \
|
||||
video/filter/vf_yadif.c \
|
||||
audio/filter/af_lavfi.c
|
||||
|
||||
@@ -123,28 +124,17 @@ SOURCES = audio/audio.c \
|
||||
audio/decode/ad_spdif.c \
|
||||
audio/decode/dec_audio.c \
|
||||
audio/filter/af.c \
|
||||
audio/filter/af_center.c \
|
||||
audio/filter/af_channels.c \
|
||||
audio/filter/af_delay.c \
|
||||
audio/filter/af_equalizer.c \
|
||||
audio/filter/af_export.c \
|
||||
audio/filter/af_extrastereo.c \
|
||||
audio/filter/af_format.c \
|
||||
audio/filter/af_hrtf.c \
|
||||
audio/filter/af_karaoke.c \
|
||||
audio/filter/af_lavcac3enc.c \
|
||||
audio/filter/af_lavrresample.c \
|
||||
audio/filter/af_pan.c \
|
||||
audio/filter/af_scaletempo.c \
|
||||
audio/filter/af_sinesuppress.c \
|
||||
audio/filter/af_sub.c \
|
||||
audio/filter/af_surround.c \
|
||||
audio/filter/af_sweep.c \
|
||||
audio/filter/af_drc.c \
|
||||
audio/filter/af_volume.c \
|
||||
audio/filter/filter.c \
|
||||
audio/filter/tools.c \
|
||||
audio/filter/window.c \
|
||||
audio/out/ao.c \
|
||||
audio/out/ao_null.c \
|
||||
audio/out/ao_pcm.c \
|
||||
@@ -207,6 +197,7 @@ SOURCES = audio/audio.c \
|
||||
player/client.c \
|
||||
player/configfiles.c \
|
||||
player/command.c \
|
||||
player/external_files.c \
|
||||
player/loadfile.c \
|
||||
player/main.c \
|
||||
player/misc.c \
|
||||
@@ -231,7 +222,6 @@ SOURCES = audio/audio.c \
|
||||
stream/stream_rar.c \
|
||||
sub/dec_sub.c \
|
||||
sub/draw_bmp.c \
|
||||
sub/find_subfiles.c \
|
||||
sub/img_convert.c \
|
||||
sub/osd.c \
|
||||
sub/sd_lavc.c \
|
||||
@@ -245,6 +235,7 @@ SOURCES = audio/audio.c \
|
||||
ta/ta_talloc.c \
|
||||
video/csputils.c \
|
||||
video/fmt-conversion.c \
|
||||
video/gpu_memcpy.c \
|
||||
video/image_writer.c \
|
||||
video/img_format.c \
|
||||
video/mp_image.c \
|
||||
|
||||
+26
-14
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import matplotlib.pyplot as plot
|
||||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
import pyqtgraph as pg
|
||||
import sys
|
||||
import re
|
||||
|
||||
@@ -38,8 +39,8 @@ Currently, the following event types are supported:
|
||||
class G:
|
||||
events = {}
|
||||
start = None
|
||||
# http://matplotlib.org/api/markers_api.html#module-matplotlib.markers
|
||||
markers = ["o", "8", "s", "p", "*", "h", "+", "x", "D"]
|
||||
markers = ["o", "s", "t", "d"]
|
||||
curveno = 0
|
||||
|
||||
def find_marker():
|
||||
if len(G.markers) == 0:
|
||||
@@ -128,20 +129,31 @@ for e, index in zip(G.sevents, range(len(G.sevents))):
|
||||
else:
|
||||
e.vals = [(x, y * (m - index) / m) for (x, y) in e.vals]
|
||||
|
||||
fig = plot.figure()
|
||||
fig.hold(True)
|
||||
pg.setConfigOption('background', 'w')
|
||||
pg.setConfigOption('foreground', 'k')
|
||||
app = QtGui.QApplication([])
|
||||
win = pg.GraphicsWindow()
|
||||
#win.resize(1500, 900)
|
||||
|
||||
ax = [None, None]
|
||||
plots = 2 if hasval else 1
|
||||
ax[0] = fig.add_subplot(plots, 1, 1)
|
||||
ax[0] = win.addPlot()
|
||||
if hasval:
|
||||
ax[1] = fig.add_subplot(plots, 1, 2, sharex=ax[0])
|
||||
legends = [[], []]
|
||||
win.nextRow()
|
||||
ax[1] = win.addPlot()
|
||||
ax[1].setXLink(ax[0])
|
||||
for cur in ax:
|
||||
if cur is not None:
|
||||
cur.addLegend(offset = (-1, 1))
|
||||
for e in G.sevents:
|
||||
cur = ax[1 if e.type == "value" else 0]
|
||||
pl, = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
|
||||
args = {'name': e.name,'antialias':True}
|
||||
if e.type == "event-signal":
|
||||
plot.setp(pl, marker = e.marker, linestyle = "None")
|
||||
for cur in ax:
|
||||
if cur is not None:
|
||||
cur.legend()
|
||||
plot.show()
|
||||
args['symbol'] = e.marker
|
||||
args['pen'] = None
|
||||
else:
|
||||
args['pen'] = pg.mkPen(pg.intColor(G.curveno), width=0)
|
||||
G.curveno += 1
|
||||
n = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], **args)
|
||||
|
||||
QtGui.QApplication.instance().exec_()
|
||||
+2
-2
@@ -232,8 +232,8 @@ sub call_mpv {
|
||||
my $output = `"$mpv" --no-config $cmd`;
|
||||
if ($? == -1) {
|
||||
die "Could not run mpv: $!";
|
||||
} elsif ($? != 0) {
|
||||
die "mpv returned " . ($? >> 8) . " with output:\n$output";
|
||||
} elsif ((my $exit_code = $? >> 8) != 0) {
|
||||
die "mpv returned $exit_code with output:\n$output";
|
||||
}
|
||||
return split /\n/, $output;
|
||||
}
|
||||
+7
-12
@@ -58,13 +58,6 @@ void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels)
|
||||
update_redundant_info(mpa);
|
||||
}
|
||||
|
||||
// Use old MPlayer/ALSA channel layout.
|
||||
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels)
|
||||
{
|
||||
mp_chmap_from_channels_alsa(&mpa->channels, num_channels);
|
||||
update_redundant_info(mpa);
|
||||
}
|
||||
|
||||
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap)
|
||||
{
|
||||
mpa->channels = *chmap;
|
||||
@@ -330,21 +323,23 @@ struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe)
|
||||
}
|
||||
|
||||
// If we can't handle the format (e.g. too many channels), bail out.
|
||||
if (!mp_audio_config_valid(new) || avframe->nb_extended_buf)
|
||||
if (!mp_audio_config_valid(new))
|
||||
goto fail;
|
||||
|
||||
for (int n = 0; n < AV_NUM_DATA_POINTERS; n++) {
|
||||
if (!avframe->buf[n])
|
||||
for (int n = 0; n < AV_NUM_DATA_POINTERS + avframe->nb_extended_buf; n++) {
|
||||
AVBufferRef *buf = n < AV_NUM_DATA_POINTERS ? avframe->buf[n]
|
||||
: avframe->extended_buf[n - AV_NUM_DATA_POINTERS];
|
||||
if (!buf)
|
||||
break;
|
||||
if (n >= MP_NUM_CHANNELS)
|
||||
goto fail;
|
||||
new->allocated[n] = av_buffer_ref(avframe->buf[n]);
|
||||
new->allocated[n] = av_buffer_ref(buf);
|
||||
if (!new->allocated[n])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (int n = 0; n < new->num_planes; n++)
|
||||
new->planes[n] = avframe->data[n];
|
||||
new->planes[n] = avframe->extended_data[n];
|
||||
new->samples = avframe->nb_samples;
|
||||
|
||||
return new;
|
||||
|
||||
@@ -46,7 +46,6 @@ struct mp_audio {
|
||||
|
||||
void mp_audio_set_format(struct mp_audio *mpa, int format);
|
||||
void mp_audio_set_num_channels(struct mp_audio *mpa, int num_channels);
|
||||
void mp_audio_set_channels_old(struct mp_audio *mpa, int num_channels);
|
||||
void mp_audio_set_channels(struct mp_audio *mpa, const struct mp_chmap *chmap);
|
||||
void mp_audio_copy_config(struct mp_audio *dst, const struct mp_audio *src);
|
||||
bool mp_audio_config_equals(const struct mp_audio *a, const struct mp_audio *b);
|
||||
|
||||
@@ -119,6 +119,15 @@ void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples)
|
||||
mp_audio_fill_silence(ab->buffer, 0, samples);
|
||||
}
|
||||
|
||||
void mp_audio_buffer_duplicate(struct mp_audio_buffer *ab, int samples)
|
||||
{
|
||||
assert(samples >= 0 && samples <= ab->buffer->samples);
|
||||
int oldlen = ab->buffer->samples;
|
||||
ab->buffer->samples += samples;
|
||||
mp_audio_realloc_min(ab->buffer, ab->buffer->samples);
|
||||
mp_audio_copy(ab->buffer, oldlen, ab->buffer, oldlen - samples, samples);
|
||||
}
|
||||
|
||||
// Get the start of the current readable buffer.
|
||||
void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ void mp_audio_buffer_get_write_buffer(struct mp_audio_buffer *ab, int minsamples
|
||||
void mp_audio_buffer_finish_write(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_append(struct mp_audio_buffer *ab, struct mp_audio *mpa);
|
||||
void mp_audio_buffer_prepend_silence(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_duplicate(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_peek(struct mp_audio_buffer *ab, struct mp_audio *out_mpa);
|
||||
void mp_audio_buffer_skip(struct mp_audio_buffer *ab, int samples);
|
||||
void mp_audio_buffer_clear(struct mp_audio_buffer *ab);
|
||||
|
||||
+10
-2
@@ -298,12 +298,18 @@ void mp_chmap_remove_useless_channels(struct mp_chmap *map,
|
||||
// Speakers not representable by ffmpeg/libav are dropped.
|
||||
// Warning: this ignores the order of the channels, and will return a channel
|
||||
// mask even if the order is different from libavcodec's.
|
||||
// Also, "unknown" channel maps are translated to non-sense channel
|
||||
// maps with the same number of channels.
|
||||
uint64_t mp_chmap_to_lavc_unchecked(const struct mp_chmap *src)
|
||||
{
|
||||
// lavc has no concept for unknown layouts yet, so pick a default
|
||||
struct mp_chmap t = *src;
|
||||
if (t.num > 64)
|
||||
return 0;
|
||||
// lavc has no concept for unknown layouts yet, so pick something that does
|
||||
// the job of signaling the number of channels, even if it makes no sense
|
||||
// as a proper layout.
|
||||
if (mp_chmap_is_unknown(&t))
|
||||
mp_chmap_from_channels(&t, t.num);
|
||||
return t.num == 64 ? (uint64_t)-1 : (1ULL << t.num) - 1;
|
||||
uint64_t mask = 0;
|
||||
for (int n = 0; n < t.num; n++) {
|
||||
if (t.speaker[n] < 64) // ignore MP_SPEAKER_ID_NA etc.
|
||||
@@ -359,6 +365,8 @@ bool mp_chmap_is_lavc(const struct mp_chmap *src)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Warning: for "unknown" channel maps, this returns something that may not
|
||||
// make sense. Invalid channel maps are not changed.
|
||||
void mp_chmap_reorder_to_lavc(struct mp_chmap *map)
|
||||
{
|
||||
if (!mp_chmap_is_valid(map))
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "misc/bstr.h"
|
||||
|
||||
#define MP_NUM_CHANNELS 8
|
||||
#define MP_NUM_CHANNELS 16
|
||||
|
||||
// Speaker a channel can be assigned to.
|
||||
// This corresponds to WAVEFORMATEXTENSIBLE channel mask bit indexes.
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <limits.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/msg.h"
|
||||
#include "chmap_sel.h"
|
||||
|
||||
static const struct mp_chmap speaker_replacements[][2] = {
|
||||
@@ -357,3 +358,24 @@ bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
|
||||
}
|
||||
return map->num > 0;
|
||||
}
|
||||
|
||||
// Print the set of allowed channel layouts.
|
||||
void mp_chmal_sel_log(const struct mp_chmap_sel *s, struct mp_log *log, int lev)
|
||||
{
|
||||
if (!mp_msg_test(log, lev))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < s->num_chmaps; i++)
|
||||
mp_msg(log, lev, " - %s\n", mp_chmap_to_str(&s->chmaps[i]));
|
||||
for (int i = 0; i < MP_SPEAKER_ID_COUNT; i++) {
|
||||
if (!s->speakers[i])
|
||||
continue;
|
||||
struct mp_chmap l = {.num = 1, .speaker = { i }};
|
||||
mp_msg(log, lev, " - #%s\n",
|
||||
i == MP_SPEAKER_ID_FC ? "fc" : mp_chmap_to_str_hr(&l));
|
||||
}
|
||||
if (s->allow_waveext)
|
||||
mp_msg(log, lev, " - waveext\n");
|
||||
if (s->allow_any)
|
||||
mp_msg(log, lev, " - anything\n");
|
||||
}
|
||||
@@ -45,4 +45,7 @@ bool mp_chmap_sel_fallback(const struct mp_chmap_sel *s, struct mp_chmap *map);
|
||||
bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
|
||||
int num);
|
||||
|
||||
struct mp_log;
|
||||
void mp_chmal_sel_log(const struct mp_chmap_sel *s, struct mp_log *log, int lev);
|
||||
|
||||
#endif
|
||||
@@ -30,7 +30,7 @@ struct dec_audio {
|
||||
struct mp_log *log;
|
||||
struct MPOpts *opts;
|
||||
struct mpv_global *global;
|
||||
bool spdif_passthrough;
|
||||
bool spdif_passthrough, spdif_failed;
|
||||
const struct ad_functions *ad_driver;
|
||||
struct sh_stream *header;
|
||||
struct af_stream *afilter;
|
||||
|
||||
+15
-41
@@ -34,23 +34,12 @@
|
||||
extern const struct af_info af_info_delay;
|
||||
extern const struct af_info af_info_channels;
|
||||
extern const struct af_info af_info_format;
|
||||
extern const struct af_info af_info_force;
|
||||
extern const struct af_info af_info_volume;
|
||||
extern const struct af_info af_info_equalizer;
|
||||
extern const struct af_info af_info_pan;
|
||||
extern const struct af_info af_info_surround;
|
||||
extern const struct af_info af_info_sub;
|
||||
extern const struct af_info af_info_export;
|
||||
extern const struct af_info af_info_drc;
|
||||
extern const struct af_info af_info_extrastereo;
|
||||
extern const struct af_info af_info_lavcac3enc;
|
||||
extern const struct af_info af_info_lavrresample;
|
||||
extern const struct af_info af_info_sweep;
|
||||
extern const struct af_info af_info_hrtf;
|
||||
extern const struct af_info af_info_ladspa;
|
||||
extern const struct af_info af_info_center;
|
||||
extern const struct af_info af_info_sinesuppress;
|
||||
extern const struct af_info af_info_karaoke;
|
||||
extern const struct af_info af_info_scaletempo;
|
||||
extern const struct af_info af_info_bs2b;
|
||||
extern const struct af_info af_info_lavfi;
|
||||
@@ -63,28 +52,13 @@ static const struct af_info *const filter_list[] = {
|
||||
&af_info_volume,
|
||||
&af_info_equalizer,
|
||||
&af_info_pan,
|
||||
&af_info_surround,
|
||||
&af_info_sub,
|
||||
&af_info_export,
|
||||
&af_info_drc,
|
||||
&af_info_extrastereo,
|
||||
&af_info_lavcac3enc,
|
||||
&af_info_lavrresample,
|
||||
&af_info_sweep,
|
||||
&af_info_hrtf,
|
||||
#if HAVE_LADSPA
|
||||
&af_info_ladspa,
|
||||
#endif
|
||||
#if HAVE_RUBBERBAND
|
||||
&af_info_rubberband,
|
||||
#endif
|
||||
&af_info_center,
|
||||
&af_info_sinesuppress,
|
||||
&af_info_karaoke,
|
||||
&af_info_scaletempo,
|
||||
#if HAVE_LIBBS2B
|
||||
&af_info_bs2b,
|
||||
#endif
|
||||
#if HAVE_LIBAVFILTER
|
||||
&af_info_lavfi,
|
||||
#endif
|
||||
@@ -185,24 +159,11 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
|
||||
MP_ERR(s, "Couldn't find audio filter '%s'.\n", name);
|
||||
return NULL;
|
||||
}
|
||||
const struct af_info *info = desc.p;
|
||||
/* Make sure that the filter is not already in the list if it is
|
||||
non-reentrant */
|
||||
if (info->flags & AF_FLAGS_NOT_REENTRANT) {
|
||||
for (struct af_instance *cur = s->first; cur; cur = cur->next) {
|
||||
if (cur->info == info) {
|
||||
MP_ERR(s, "There can only be one "
|
||||
"instance of the filter '%s' in each stream\n", name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP_VERBOSE(s, "Adding filter %s \n", name);
|
||||
|
||||
struct af_instance *af = talloc_zero(NULL, struct af_instance);
|
||||
*af = (struct af_instance) {
|
||||
.info = info,
|
||||
.info = desc.p,
|
||||
.data = talloc_zero(af, struct mp_audio),
|
||||
.log = mp_log_new(af, s->log, name),
|
||||
.replaygain_data = s->replaygain_data,
|
||||
@@ -500,7 +461,8 @@ static int af_reinit(struct af_stream *s)
|
||||
struct af_instance *aft = af->prev;
|
||||
af_remove(s, af);
|
||||
af = aft->next;
|
||||
break;
|
||||
retry++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
goto negotiate_error;
|
||||
@@ -721,6 +683,18 @@ void af_control_all(struct af_stream *s, int cmd, void *arg)
|
||||
af->control(af, cmd, arg);
|
||||
}
|
||||
|
||||
int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label)
|
||||
{
|
||||
char *label_str = bstrdup0(NULL, label);
|
||||
struct af_instance *cur = af_find_by_label(s, label_str);
|
||||
talloc_free(label_str);
|
||||
if (cur) {
|
||||
return cur->control ? cur->control(cur, cmd, arg) : CONTROL_NA;
|
||||
} else {
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// Used by filters to add a filtered frame to the output queue.
|
||||
// Ownership of frame is transferred from caller to the filter chain.
|
||||
void af_add_output_frame(struct af_instance *af, struct mp_audio *frame)
|
||||
|
||||
+9
-12
@@ -27,6 +27,7 @@
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/audio.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/common.h"
|
||||
|
||||
struct af_instance;
|
||||
struct mpv_global;
|
||||
@@ -34,10 +35,6 @@ struct mpv_global;
|
||||
// Number of channels
|
||||
#define AF_NCH MP_NUM_CHANNELS
|
||||
|
||||
// Flags used for defining the behavior of an audio filter
|
||||
#define AF_FLAGS_REENTRANT 0x00000000
|
||||
#define AF_FLAGS_NOT_REENTRANT 0x00000001
|
||||
|
||||
// Flags for af->filter()
|
||||
#define AF_FILTER_FLAG_EOF 1
|
||||
|
||||
@@ -46,7 +43,6 @@ struct mpv_global;
|
||||
struct af_info {
|
||||
const char *info;
|
||||
const char *name;
|
||||
const int flags;
|
||||
int (*open)(struct af_instance *vf);
|
||||
int priv_size;
|
||||
const void *priv_defaults;
|
||||
@@ -104,13 +100,12 @@ struct af_stream {
|
||||
};
|
||||
|
||||
// Return values
|
||||
#define AF_DETACH 2
|
||||
#define AF_OK 1
|
||||
#define AF_TRUE 1
|
||||
#define AF_FALSE 0
|
||||
#define AF_UNKNOWN -1
|
||||
#define AF_ERROR -2
|
||||
#define AF_FATAL -3
|
||||
#define AF_DETACH (CONTROL_OK + 1)
|
||||
#define AF_OK CONTROL_OK
|
||||
#define AF_TRUE CONTROL_TRUE
|
||||
#define AF_FALSE CONTROL_FALSE
|
||||
#define AF_UNKNOWN CONTROL_UNKNOWN
|
||||
#define AF_ERROR CONTROL_ERROR
|
||||
|
||||
// Parameters for af_control_*
|
||||
enum af_control {
|
||||
@@ -127,6 +122,7 @@ enum af_control {
|
||||
AF_CONTROL_GET_PAN_BALANCE,
|
||||
AF_CONTROL_SET_PLAYBACK_SPEED,
|
||||
AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE,
|
||||
AF_CONTROL_GET_METADATA,
|
||||
};
|
||||
|
||||
// Argument for AF_CONTROL_SET_PAN_LEVEL
|
||||
@@ -145,6 +141,7 @@ int af_remove_by_label(struct af_stream *s, char *label);
|
||||
struct af_instance *af_find_by_label(struct af_stream *s, char *label);
|
||||
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
|
||||
void af_control_all(struct af_stream *s, int cmd, void *arg);
|
||||
int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label);
|
||||
void af_seek_reset(struct af_stream *s);
|
||||
|
||||
void af_add_output_frame(struct af_instance *af, struct mp_audio *frame);
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* The Bauer stereophonic-to-binaural DSP using bs2b library:
|
||||
* http://bs2b.sourceforge.net/
|
||||
*
|
||||
* Copyright (c) 2009 Andrew Savchenko
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <bs2b.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "af.h"
|
||||
#include "options/m_option.h"
|
||||
|
||||
/// Internal specific data of the filter
|
||||
struct af_bs2b {
|
||||
int fcut; ///< cut frequency in Hz
|
||||
int feed; ///< feed level for low frequencies in 0.1*dB
|
||||
int profile ; ///< profile (available crossfeed presets)
|
||||
t_bs2bdp filter; ///< instance of a library filter
|
||||
};
|
||||
|
||||
#define DEF_FILTER(fmt, name) \
|
||||
static int filter_##name(struct af_instance *af, struct mp_audio *data) \
|
||||
{ \
|
||||
if (!data) \
|
||||
return 0; \
|
||||
if (af_make_writeable(af, data) < 0) { \
|
||||
talloc_free(data); \
|
||||
return -1; \
|
||||
} \
|
||||
bs2b_cross_feed_##name(((struct af_bs2b*)(af->priv))->filter, \
|
||||
(data->planes[0]), data->samples); \
|
||||
af_add_output_frame(af, data); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define GET_FILTER(fmt, name) \
|
||||
case AF_FORMAT_##fmt: return filter_##name;
|
||||
|
||||
#define FILTERS \
|
||||
FILTER(FLOAT, f) \
|
||||
FILTER(S32, s32) \
|
||||
FILTER(S24, s24) \
|
||||
FILTER(S16, s16) \
|
||||
FILTER(U8, u8)
|
||||
|
||||
#define FILTER DEF_FILTER
|
||||
FILTERS
|
||||
#undef FILTER
|
||||
|
||||
typedef int (*filter)(struct af_instance *af, struct mp_audio *d);
|
||||
static filter get_filter(int fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
#define FILTER GET_FILTER
|
||||
FILTERS
|
||||
#undef FILTER
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialization and runtime control
|
||||
static int control(struct af_instance *af, int cmd, void *arg)
|
||||
{
|
||||
struct af_bs2b *s = af->priv;
|
||||
|
||||
switch (cmd) {
|
||||
case AF_CONTROL_REINIT: {
|
||||
int format;
|
||||
// Sanity check
|
||||
if (!arg) return AF_ERROR;
|
||||
|
||||
format = ((struct mp_audio*)arg)->format;
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_set_num_channels(af->data, 2); // bs2b is useful only for 2ch audio
|
||||
mp_audio_set_format(af->data, format);
|
||||
|
||||
/* check for formats supported by libbs2b
|
||||
and assign corresponding handlers */
|
||||
af->filter_frame = get_filter(format);
|
||||
if (!af->filter_frame) {
|
||||
af->filter_frame = filter_f;
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
}
|
||||
|
||||
// bs2b have srate limits, try to resample if needed
|
||||
if (af->data->rate > BS2B_MAXSRATE || af->data->rate < BS2B_MINSRATE) {
|
||||
af->data->rate = BS2B_DEFAULT_SRATE;
|
||||
MP_WARN(af, "Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n"
|
||||
"Trying to resample to %d Hz.\n",
|
||||
af->data->rate, BS2B_MINSRATE, BS2B_MAXSRATE, BS2B_DEFAULT_SRATE);
|
||||
}
|
||||
bs2b_set_srate(s->filter, (long)af->data->rate);
|
||||
MP_VERBOSE(af, "using format %s\n",
|
||||
af_fmt_to_str(af->data->format));
|
||||
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
/// Deallocate memory and close library
|
||||
static void uninit(struct af_instance *af)
|
||||
{
|
||||
struct af_bs2b *s = af->priv;
|
||||
if (s->filter)
|
||||
bs2b_close(s->filter);
|
||||
}
|
||||
|
||||
/// Allocate memory, set function pointers and init library
|
||||
static int af_open(struct af_instance *af)
|
||||
{
|
||||
struct af_bs2b *s = af->priv;
|
||||
af->control = control;
|
||||
af->uninit = uninit;
|
||||
|
||||
// NULL means failed initialization
|
||||
if (!(s->filter = bs2b_open())) {
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
if (s->profile)
|
||||
bs2b_set_level(s->filter, s->profile);
|
||||
// set fcut and feed only if specified, otherwise defaults will be used
|
||||
if (s->fcut)
|
||||
bs2b_set_level_fcut(s->filter, s->fcut);
|
||||
if (s->feed)
|
||||
bs2b_set_level_feed(s->filter, s->feed);
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct af_bs2b
|
||||
|
||||
/// Description of this filter
|
||||
const struct af_info af_info_bs2b = {
|
||||
.info = "Bauer stereophonic-to-binaural audio filter",
|
||||
.name = "bs2b",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(struct af_bs2b),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INTRANGE("fcut", fcut, 0, BS2B_MINFCUT, BS2B_MAXFCUT),
|
||||
OPT_INTRANGE("feed", feed, 0, BS2B_MINFEED, BS2B_MAXFEED),
|
||||
OPT_CHOICE("profile", profile, 0,
|
||||
({"unset", 0},
|
||||
{"default", BS2B_DEFAULT_CLEVEL},
|
||||
{"cmoy", BS2B_CMOY_CLEVEL},
|
||||
{"jmeier", BS2B_JMEIER_CLEVEL})),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* This filter adds a center channel to the audio stream by
|
||||
* averaging the left and right channel.
|
||||
* There are two runtime controls one for setting which channel
|
||||
* to insert the center-audio into called AF_CONTROL_SUB_CH.
|
||||
*
|
||||
* FIXME: implement a high-pass filter for better results.
|
||||
*
|
||||
* copyright (c) 2005 Alex Beregszaszi
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_center_s
|
||||
{
|
||||
int ch; // Channel number which to insert the filtered data
|
||||
}af_center_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_center_t* s = af->priv;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_set_channels_old(af->data, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance* af, struct mp_audio* data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
if (af_make_writeable(af, data) < 0) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_center_t* s = af->priv; // Setup for this instance
|
||||
float* a = c->planes[0]; // Audio data
|
||||
int nch = c->nch; // Number of channels
|
||||
int len = c->samples*c->nch; // Number of samples in current audio block
|
||||
int ch = s->ch; // Channel in which to insert the center audio
|
||||
register int i;
|
||||
|
||||
// Run filter
|
||||
for(i=0;i<len;i+=nch){
|
||||
// Average left and right
|
||||
a[i+ch] = (a[i]/2) + (a[i+1]/2);
|
||||
}
|
||||
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = filter_frame;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_center_t
|
||||
const struct af_info af_info_center = {
|
||||
.info = "Audio filter for adding a center channel",
|
||||
.name = "center",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_center_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INTRANGE("channel", ch, 0, 0, AF_NCH - 1, OPTDEF_INT(1)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -324,7 +324,6 @@ static int af_open(struct af_instance* af){
|
||||
const struct af_info af_info_drc = {
|
||||
.info = "Dynamic range compression filter",
|
||||
.name = "drc",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_drc_t),
|
||||
.options = (const struct m_option[]) {
|
||||
|
||||
@@ -203,7 +203,6 @@ static int af_open(struct af_instance* af){
|
||||
const struct af_info af_info_equalizer = {
|
||||
.info = "Equalizer audio filter",
|
||||
.name = "equalizer",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_equalizer_t),
|
||||
.options = (const struct m_option[]) {
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* This audio filter exports the incoming signal to other processes
|
||||
* using memory mapping. The memory mapped area contains a header:
|
||||
* int nch,
|
||||
* int size,
|
||||
* unsigned long long counter (updated every time the contents of
|
||||
* the area changes),
|
||||
* the rest is payload (non-interleaved).
|
||||
*
|
||||
* Authors: Anders; Gustavo Sverzut Barbieri <gustavo.barbieri@ic.unicamp.br>
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "osdep/io.h"
|
||||
|
||||
#include "talloc.h"
|
||||
#include "af.h"
|
||||
#include "options/path.h"
|
||||
|
||||
#define DEF_SZ 512 // default buffer size (in samples)
|
||||
#define SHARED_FILE "mpv-af_export" /* default file name
|
||||
(relative to ~/.mpv/ */
|
||||
|
||||
#define SIZE_HEADER (2 * sizeof(int) + sizeof(unsigned long long))
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_export_s
|
||||
{
|
||||
unsigned long long count; // Used for sync
|
||||
void* buf[AF_NCH]; // Buffers for storing the data before it is exported
|
||||
int sz; // Size of buffer in samples
|
||||
int wi; // Write index
|
||||
int fd; // File descriptor to shared memory area
|
||||
char* filename; // File to export data
|
||||
uint8_t *mmap_area; // MMap shared area
|
||||
} af_export_t;
|
||||
|
||||
|
||||
/* Initialization and runtime control
|
||||
af audio filter instance
|
||||
cmd control command
|
||||
arg argument
|
||||
*/
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_export_t* s = af->priv;
|
||||
switch (cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
int i=0;
|
||||
int mapsize;
|
||||
|
||||
// Free previous buffers
|
||||
free(s->buf[0]);
|
||||
|
||||
// unmap previous area
|
||||
if(s->mmap_area)
|
||||
munmap(s->mmap_area, SIZE_HEADER + (af->data->bps*s->sz*af->data->nch));
|
||||
// close previous file descriptor
|
||||
if(s->fd)
|
||||
close(s->fd);
|
||||
|
||||
// Accept only int16_t as input format (which sucks)
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16);
|
||||
|
||||
// Allocate new buffers (as one continuous block)
|
||||
s->buf[0] = calloc(s->sz*af->data->nch, af->data->bps);
|
||||
if(NULL == s->buf[0]) {
|
||||
MP_FATAL(af, "Out of memory\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
for(i = 1; i < af->data->nch; i++)
|
||||
s->buf[i] = (uint8_t *)s->buf[0] + i*s->sz*af->data->bps;
|
||||
|
||||
if (!s->filename) {
|
||||
MP_FATAL(af, "No filename set.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
// Init memory mapping
|
||||
s->fd = open(s->filename, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
|
||||
MP_INFO(af, "Exporting to file: %s\n", s->filename);
|
||||
if(s->fd < 0) {
|
||||
MP_FATAL(af, "Could not open/create file: %s\n",
|
||||
s->filename);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
// header + buffer
|
||||
mapsize = (SIZE_HEADER + (af->data->bps * s->sz * af->data->nch));
|
||||
|
||||
// grow file to needed size
|
||||
for(i = 0; i < mapsize; i++){
|
||||
char null = 0;
|
||||
write(s->fd, (void*) &null, 1);
|
||||
}
|
||||
|
||||
// mmap size
|
||||
s->mmap_area = mmap(0, mapsize, PROT_READ|PROT_WRITE,MAP_SHARED, s->fd, 0);
|
||||
if(s->mmap_area == NULL)
|
||||
MP_FATAL(af, "Could not mmap file %s\n", s->filename);
|
||||
MP_INFO(af, "Memory mapped to file: %s (%p)\n",
|
||||
s->filename, s->mmap_area);
|
||||
|
||||
// Initialize header
|
||||
*((int*)s->mmap_area) = af->data->nch;
|
||||
*((int*)s->mmap_area + 1) = s->sz * af->data->bps * af->data->nch;
|
||||
msync(s->mmap_area, mapsize, MS_ASYNC);
|
||||
|
||||
// Use test_output to return FALSE if necessary
|
||||
return af_test_output(af, (struct mp_audio*)arg);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
/* Free allocated memory and clean up other stuff too.
|
||||
af audio filter instance
|
||||
*/
|
||||
static void uninit( struct af_instance* af )
|
||||
{
|
||||
af_export_t* s = af->priv;
|
||||
|
||||
free(s->buf[0]);
|
||||
|
||||
// Free mmaped area
|
||||
if(s->mmap_area)
|
||||
munmap(s->mmap_area, sizeof(af_export_t));
|
||||
|
||||
if(s->fd > -1)
|
||||
close(s->fd);
|
||||
}
|
||||
|
||||
/* Filter data through filter
|
||||
af audio filter instance
|
||||
data audio data
|
||||
*/
|
||||
static int filter(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_export_t* s = af->priv; // Setup for this instance
|
||||
int16_t* a = c->planes[0]; // Incoming sound
|
||||
int nch = c->nch; // Number of channels
|
||||
int len = c->samples*c->nch; // Number of sample in data chunk
|
||||
int sz = s->sz; // buffer size (in samples)
|
||||
int flag = 0; // Set to 1 if buffer is filled
|
||||
|
||||
int ch, i;
|
||||
|
||||
// Fill all buffers
|
||||
for(ch = 0; ch < nch; ch++){
|
||||
int wi = s->wi; // Reset write index
|
||||
int16_t* b = s->buf[ch]; // Current buffer
|
||||
|
||||
// Copy data to export buffers
|
||||
for(i = ch; i < len; i += nch){
|
||||
b[wi++] = a[i];
|
||||
if(wi >= sz){ // Don't write outside the end of the buffer
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
s->wi = wi % s->sz;
|
||||
}
|
||||
|
||||
// Export buffer to mmaped area
|
||||
if(flag){
|
||||
// update buffer in mapped area
|
||||
memcpy(s->mmap_area + SIZE_HEADER, s->buf[0], sz * c->bps * nch);
|
||||
s->count++; // increment counter (to sync)
|
||||
memcpy(s->mmap_area + SIZE_HEADER - sizeof(s->count),
|
||||
&(s->count), sizeof(s->count));
|
||||
}
|
||||
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate memory and set function pointers
|
||||
af audio filter instance
|
||||
returns AF_OK or AF_ERROR
|
||||
*/
|
||||
static int af_open( struct af_instance* af )
|
||||
{
|
||||
af->control = control;
|
||||
af->uninit = uninit;
|
||||
af->filter_frame = filter;
|
||||
af_export_t *priv = af->priv;
|
||||
|
||||
if (!priv->filename || !priv->filename[0]) {
|
||||
MP_FATAL(af, "no export filename given");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_export_t
|
||||
const struct af_info af_info_export = {
|
||||
.info = "Sound export filter",
|
||||
.name = "export",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_export_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("filename", filename, 0),
|
||||
OPT_INTRANGE("buffersamples", sz, 0, 1, 2048, OPTDEF_INT(DEF_SZ)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_extrastereo_s
|
||||
{
|
||||
float mul;
|
||||
}af_extrastereo_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_force_interleaved_format(af->data);
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
if (af->data->format != AF_FORMAT_FLOAT)
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16);
|
||||
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static void play_s16(af_extrastereo_t *s, struct mp_audio* data)
|
||||
{
|
||||
register int i = 0;
|
||||
int16_t *a = (int16_t*)data->planes[0]; // Audio data
|
||||
int len = data->samples*data->nch; // Number of samples
|
||||
int avg, l, r;
|
||||
|
||||
for (i = 0; i < len; i+=2)
|
||||
{
|
||||
avg = (a[i] + a[i + 1]) / 2;
|
||||
|
||||
l = avg + (int)(s->mul * (a[i] - avg));
|
||||
r = avg + (int)(s->mul * (a[i + 1] - avg));
|
||||
|
||||
a[i] = MPCLAMP(l, SHRT_MIN, SHRT_MAX);
|
||||
a[i + 1] = MPCLAMP(r, SHRT_MIN, SHRT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
static void play_float(af_extrastereo_t *s, struct mp_audio* data)
|
||||
{
|
||||
register int i = 0;
|
||||
float *a = (float*)data->planes[0]; // Audio data
|
||||
int len = data->samples * data->nch; // Number of samples
|
||||
float avg, l, r;
|
||||
|
||||
for (i = 0; i < len; i+=2)
|
||||
{
|
||||
avg = (a[i] + a[i + 1]) / 2;
|
||||
|
||||
l = avg + (s->mul * (a[i] - avg));
|
||||
r = avg + (s->mul * (a[i + 1] - avg));
|
||||
|
||||
a[i] = af_softclip(l);
|
||||
a[i + 1] = af_softclip(r);
|
||||
}
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
if (af_make_writeable(af, data) < 0) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
if (data->format == AF_FORMAT_FLOAT) {
|
||||
play_float(af->priv, data);
|
||||
} else {
|
||||
play_s16(af->priv, data);
|
||||
}
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = filter_frame;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_extrastereo_t
|
||||
const struct af_info af_info_extrastereo = {
|
||||
.info = "Increase difference between audio channels",
|
||||
.name = "extrastereo",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_extrastereo_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLOAT("mul", mul, 0, OPTDEF_FLOAT(2.5)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,670 +0,0 @@
|
||||
/*
|
||||
* Experimental audio filter that mixes 5.1 and 5.1 with matrix
|
||||
* encoded rear channels into headphone signal using FIR filtering
|
||||
* with HRTF.
|
||||
*
|
||||
* Author: ylai
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "af.h"
|
||||
#include "dsp.h"
|
||||
|
||||
/* HRTF filter coefficients and adjustable parameters */
|
||||
#include "af_hrtf.h"
|
||||
|
||||
typedef struct af_hrtf_s {
|
||||
/* Lengths */
|
||||
int dlbuflen, hrflen, basslen;
|
||||
/* L, C, R, Ls, Rs channels */
|
||||
float *lf, *rf, *lr, *rr, *cf, *cr;
|
||||
const float *cf_ir, *af_ir, *of_ir, *ar_ir, *or_ir, *cr_ir;
|
||||
int cf_o, af_o, of_o, ar_o, or_o, cr_o;
|
||||
/* Bass */
|
||||
float *ba_l, *ba_r;
|
||||
float *ba_ir;
|
||||
/* Whether to matrix decode the rear center channel */
|
||||
int matrix_mode;
|
||||
/* How to decode the input:
|
||||
0 = 5/5+1 channels
|
||||
1 = 2 channels
|
||||
2 = matrix encoded 2 channels */
|
||||
int decode_mode;
|
||||
/* Full wave rectified (FWR) amplitudes and gain used to steer the
|
||||
active matrix decoding of front channels (variable names
|
||||
lpr/lmr means Lt + Rt, Lt - Rt) */
|
||||
float l_fwr, r_fwr, lpr_fwr, lmr_fwr;
|
||||
float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain;
|
||||
/* Matrix input decoding require special FWR buffer, since the
|
||||
decoding is done in place. */
|
||||
float *fwrbuf_l, *fwrbuf_r, *fwrbuf_lr, *fwrbuf_rr;
|
||||
/* Rear channel delay buffer for matrix decoding */
|
||||
float *rear_dlbuf;
|
||||
/* Full wave rectified amplitude and gain used to steer the active
|
||||
matrix decoding of center rear channel */
|
||||
float lr_fwr, rr_fwr, lrprr_fwr, lrmrr_fwr;
|
||||
float adapt_lr_gain, adapt_rr_gain;
|
||||
float adapt_lrprr_gain, adapt_lrmrr_gain;
|
||||
/* Cyclic position on the ring buffer */
|
||||
int cyc_pos;
|
||||
int print_flag;
|
||||
int mode;
|
||||
} af_hrtf_t;
|
||||
|
||||
/* Convolution on a ring buffer
|
||||
* nx: length of the ring buffer
|
||||
* nk: length of the convolution kernel
|
||||
* sx: ring buffer
|
||||
* sk: convolution kernel
|
||||
* offset: offset on the ring buffer, can be
|
||||
*/
|
||||
static float conv(const int nx, const int nk, const float *sx, const float *sk,
|
||||
const int offset)
|
||||
{
|
||||
/* k = reminder of offset / nx */
|
||||
int k = offset >= 0 ? offset % nx : nx + (offset % nx);
|
||||
|
||||
if(nk + k <= nx)
|
||||
return af_filter_fir(nk, sx + k, sk);
|
||||
else
|
||||
return af_filter_fir(nk + k - nx, sx, sk + nx - k) +
|
||||
af_filter_fir(nx - k, sx + k, sk);
|
||||
}
|
||||
|
||||
/* Detect when the impulse response starts (significantly) */
|
||||
static int pulse_detect(const float *sx)
|
||||
{
|
||||
/* nmax must be the reference impulse response length (128) minus
|
||||
s->hrflen */
|
||||
const int nmax = 128 - HRTFFILTLEN;
|
||||
const float thresh = IRTHRESH;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nmax; i++)
|
||||
if(fabs(sx[i]) > thresh)
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fuzzy matrix coefficient transfer function to "lock" the matrix on
|
||||
a effectively passive mode if the gain is approximately 1 */
|
||||
static inline float passive_lock(float x)
|
||||
{
|
||||
const float x1 = x - 1;
|
||||
const float ax1s = fabs(x - 1) * (1.0 / MATAGCLOCK);
|
||||
|
||||
return x1 - x1 / (1 + ax1s * ax1s) + 1;
|
||||
}
|
||||
|
||||
/* Unified active matrix decoder for 2 channel matrix encoded surround
|
||||
sources */
|
||||
static inline void matrix_decode(short *in, const int k, const int il,
|
||||
const int ir, const int decode_rear,
|
||||
const int dlbuflen,
|
||||
float l_fwr, float r_fwr,
|
||||
float lpr_fwr, float lmr_fwr,
|
||||
float *adapt_l_gain, float *adapt_r_gain,
|
||||
float *adapt_lpr_gain, float *adapt_lmr_gain,
|
||||
float *lf, float *rf, float *lr,
|
||||
float *rr, float *cf)
|
||||
{
|
||||
const int kr = (k + MATREARDELAY) % dlbuflen;
|
||||
float l_gain = (l_fwr + r_fwr) /
|
||||
(1 + l_fwr + l_fwr);
|
||||
float r_gain = (l_fwr + r_fwr) /
|
||||
(1 + r_fwr + r_fwr);
|
||||
/* The 2nd axis has strong gain fluctuations, and therefore require
|
||||
limits. The factor corresponds to the 1 / amplification of (Lt
|
||||
- Rt) when (Lt, Rt) is strongly correlated. (e.g. during
|
||||
dialogues). It should be bigger than -12 dB to prevent
|
||||
distortion. */
|
||||
float lmr_lim_fwr = lmr_fwr > M9_03DB * lpr_fwr ?
|
||||
lmr_fwr : M9_03DB * lpr_fwr;
|
||||
float lpr_gain = (lpr_fwr + lmr_lim_fwr) /
|
||||
(1 + lpr_fwr + lpr_fwr);
|
||||
float lmr_gain = (lpr_fwr + lmr_lim_fwr) /
|
||||
(1 + lmr_lim_fwr + lmr_lim_fwr);
|
||||
float lmr_unlim_gain = (lpr_fwr + lmr_fwr) /
|
||||
(1 + lmr_fwr + lmr_fwr);
|
||||
float lpr, lmr;
|
||||
float l_agc, r_agc, lpr_agc, lmr_agc;
|
||||
float f, d_gain, c_gain, c_agc_cfk;
|
||||
|
||||
#if 0
|
||||
static int counter = 0;
|
||||
static FILE *fp_out;
|
||||
|
||||
if(counter == 0)
|
||||
fp_out = fopen("af_hrtf.log", "w");
|
||||
if(counter % 240 == 0)
|
||||
fprintf(fp_out, "%g %g %g %g %g ", counter * (1.0 / 48000),
|
||||
l_gain, r_gain, lpr_gain, lmr_gain);
|
||||
#endif
|
||||
|
||||
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
|
||||
/* AGC adaption */
|
||||
d_gain = (fabs(l_gain - *adapt_l_gain) +
|
||||
fabs(r_gain - *adapt_r_gain)) * 0.5;
|
||||
f = d_gain * (1.0 / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*adapt_l_gain = (1 - f) * *adapt_l_gain + f * l_gain;
|
||||
*adapt_r_gain = (1 - f) * *adapt_r_gain + f * r_gain;
|
||||
/* Matrix */
|
||||
l_agc = in[il] * passive_lock(*adapt_l_gain);
|
||||
r_agc = in[ir] * passive_lock(*adapt_r_gain);
|
||||
cf[k] = (l_agc + r_agc) * M_SQRT1_2;
|
||||
if(decode_rear) {
|
||||
lr[kr] = rr[kr] = (l_agc - r_agc) * M_SQRT1_2;
|
||||
/* Stereo rear channel is steered with the same AGC steering as
|
||||
the decoding matrix. Note this requires a fast updating AGC
|
||||
at the order of 20 ms (which is the case here). */
|
||||
lr[kr] *= (l_fwr + l_fwr) /
|
||||
(1 + l_fwr + r_fwr);
|
||||
rr[kr] *= (r_fwr + r_fwr) /
|
||||
(1 + l_fwr + r_fwr);
|
||||
}
|
||||
|
||||
/*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
|
||||
lpr = (in[il] + in[ir]) * M_SQRT1_2;
|
||||
lmr = (in[il] - in[ir]) * M_SQRT1_2;
|
||||
/* AGC adaption */
|
||||
d_gain = fabs(lmr_unlim_gain - *adapt_lmr_gain);
|
||||
f = d_gain * (1.0 / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*adapt_lpr_gain = (1 - f) * *adapt_lpr_gain + f * lpr_gain;
|
||||
*adapt_lmr_gain = (1 - f) * *adapt_lmr_gain + f * lmr_gain;
|
||||
/* Matrix */
|
||||
lpr_agc = lpr * passive_lock(*adapt_lpr_gain);
|
||||
lmr_agc = lmr * passive_lock(*adapt_lmr_gain);
|
||||
lf[k] = (lpr_agc + lmr_agc) * M_SQRT1_2;
|
||||
rf[k] = (lpr_agc - lmr_agc) * M_SQRT1_2;
|
||||
|
||||
/*** CENTER FRONT CANCELLATION ***/
|
||||
/* A heuristic approach exploits that Lt + Rt gain contains the
|
||||
information about Lt, Rt correlation. This effectively reshapes
|
||||
the front and rear "cones" to concentrate Lt + Rt to C and
|
||||
introduce Lt - Rt in L, R. */
|
||||
/* 0.67677 is the empirical lower bound for lpr_gain. */
|
||||
c_gain = 8 * (*adapt_lpr_gain - 0.67677);
|
||||
c_gain = c_gain > 0 ? c_gain : 0;
|
||||
/* c_gain should not be too high, not even reaching full
|
||||
cancellation (~ 0.50 - 0.55 at current AGC implementation), or
|
||||
the center will s0und too narrow. */
|
||||
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
|
||||
c_agc_cfk = c_gain * cf[k];
|
||||
lf[k] -= c_agc_cfk;
|
||||
rf[k] -= c_agc_cfk;
|
||||
cf[k] += c_agc_cfk + c_agc_cfk;
|
||||
#if 0
|
||||
if(counter % 240 == 0)
|
||||
fprintf(fp_out, "%g %g %g %g %g\n",
|
||||
*adapt_l_gain, *adapt_r_gain,
|
||||
*adapt_lpr_gain, *adapt_lmr_gain,
|
||||
c_gain);
|
||||
counter++;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void update_ch(af_hrtf_t *s, short *in, const int k)
|
||||
{
|
||||
const int fwr_pos = (k + FWRDURATION) % s->dlbuflen;
|
||||
/* Update the full wave rectified total amplitude */
|
||||
/* Input matrix decoder */
|
||||
if(s->decode_mode == HRTF_MIX_MATRIX2CH) {
|
||||
s->l_fwr += abs(in[0]) - fabs(s->fwrbuf_l[fwr_pos]);
|
||||
s->r_fwr += abs(in[1]) - fabs(s->fwrbuf_r[fwr_pos]);
|
||||
s->lpr_fwr += abs(in[0] + in[1]) -
|
||||
fabs(s->fwrbuf_l[fwr_pos] + s->fwrbuf_r[fwr_pos]);
|
||||
s->lmr_fwr += abs(in[0] - in[1]) -
|
||||
fabs(s->fwrbuf_l[fwr_pos] - s->fwrbuf_r[fwr_pos]);
|
||||
}
|
||||
/* Rear matrix decoder */
|
||||
if(s->matrix_mode) {
|
||||
s->lr_fwr += abs(in[2]) - fabs(s->fwrbuf_lr[fwr_pos]);
|
||||
s->rr_fwr += abs(in[3]) - fabs(s->fwrbuf_rr[fwr_pos]);
|
||||
s->lrprr_fwr += abs(in[2] + in[3]) -
|
||||
fabs(s->fwrbuf_lr[fwr_pos] + s->fwrbuf_rr[fwr_pos]);
|
||||
s->lrmrr_fwr += abs(in[2] - in[3]) -
|
||||
fabs(s->fwrbuf_lr[fwr_pos] - s->fwrbuf_rr[fwr_pos]);
|
||||
}
|
||||
|
||||
switch (s->decode_mode) {
|
||||
case HRTF_MIX_51:
|
||||
/* 5/5+1 channel sources */
|
||||
s->lf[k] = in[0];
|
||||
s->cf[k] = in[4];
|
||||
s->rf[k] = in[1];
|
||||
s->fwrbuf_lr[k] = s->lr[k] = in[2];
|
||||
s->fwrbuf_rr[k] = s->rr[k] = in[3];
|
||||
break;
|
||||
case HRTF_MIX_MATRIX2CH:
|
||||
/* Matrix encoded 2 channel sources */
|
||||
s->fwrbuf_l[k] = in[0];
|
||||
s->fwrbuf_r[k] = in[1];
|
||||
matrix_decode(in, k, 0, 1, 1, s->dlbuflen,
|
||||
s->l_fwr, s->r_fwr,
|
||||
s->lpr_fwr, s->lmr_fwr,
|
||||
&(s->adapt_l_gain), &(s->adapt_r_gain),
|
||||
&(s->adapt_lpr_gain), &(s->adapt_lmr_gain),
|
||||
s->lf, s->rf, s->lr, s->rr, s->cf);
|
||||
break;
|
||||
case HRTF_MIX_STEREO:
|
||||
/* Stereo sources */
|
||||
s->lf[k] = in[0];
|
||||
s->rf[k] = in[1];
|
||||
s->cf[k] = s->lr[k] = s->rr[k] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We need to update the bass compensation delay line, too. */
|
||||
// TODO: should this use lf/cf/rf etc. instead?
|
||||
s->ba_l[k] = in[0];
|
||||
s->ba_r[k] = in[1];
|
||||
if (s->decode_mode == HRTF_MIX_51) {
|
||||
s->ba_l[k] += in[4] + in[2];
|
||||
s->ba_r[k] += in[4] + 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)
|
||||
{
|
||||
af_hrtf_t *s = af->priv;
|
||||
int test_output_res;
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* Deallocate memory */
|
||||
static void uninit(struct af_instance *af)
|
||||
{
|
||||
af_hrtf_t *s = af->priv;
|
||||
|
||||
free(s->lf);
|
||||
free(s->rf);
|
||||
free(s->lr);
|
||||
free(s->rr);
|
||||
free(s->cf);
|
||||
free(s->cr);
|
||||
free(s->ba_l);
|
||||
free(s->ba_r);
|
||||
free(s->ba_ir);
|
||||
free(s->fwrbuf_l);
|
||||
free(s->fwrbuf_r);
|
||||
free(s->fwrbuf_lr);
|
||||
free(s->fwrbuf_rr);
|
||||
}
|
||||
|
||||
/* Filter data through filter
|
||||
|
||||
Two "tricks" are used to compensate the "color" of the KEMAR data:
|
||||
|
||||
1. The KEMAR data is refiltered to ensure that the front L, R channels
|
||||
on the same side of the ear are equalized (especially in the high
|
||||
frequencies).
|
||||
|
||||
2. A bass compensation is introduced to ensure that 0-200 Hz are not
|
||||
damped (without any real 3D acoustical image, however).
|
||||
*/
|
||||
static int filter(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
af_hrtf_t *s = af->priv;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
struct mp_audio *outframe =
|
||||
mp_audio_pool_get(af->out_pool, &af->fmt_out, data->samples);
|
||||
if (!outframe) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
mp_audio_copy_attributes(outframe, data);
|
||||
|
||||
short *in = data->planes[0]; // Input audio data
|
||||
short *out = outframe->planes[0]; // Output audio data
|
||||
short *end = in + data->samples * data->nch; // Loop end
|
||||
float common, left, right, diff, left_b, right_b;
|
||||
const int dblen = s->dlbuflen, hlen = s->hrflen, blen = s->basslen;
|
||||
|
||||
if(s->print_flag) {
|
||||
s->print_flag = 0;
|
||||
switch (s->decode_mode) {
|
||||
case HRTF_MIX_51:
|
||||
MP_INFO(af, "Using HRTF to mix %s discrete surround into "
|
||||
"L, R channels\n", s->matrix_mode ? "5+1" : "5");
|
||||
break;
|
||||
case HRTF_MIX_STEREO:
|
||||
MP_INFO(af, "Using HRTF to mix stereo into "
|
||||
"L, R channels\n");
|
||||
break;
|
||||
case HRTF_MIX_MATRIX2CH:
|
||||
MP_INFO(af, "Using active matrix to decode 2 channel "
|
||||
"input, HRTF to mix %s matrix surround into "
|
||||
"L, R channels\n", "3/2");
|
||||
break;
|
||||
default:
|
||||
MP_WARN(af, "bogus decode_mode: %d\n", s->decode_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
if(s->matrix_mode)
|
||||
MP_INFO(af, "Using active matrix to decode rear center "
|
||||
"channel\n");
|
||||
}
|
||||
|
||||
/* MPlayer's 5 channel layout (notation for the variable):
|
||||
*
|
||||
* 0: L (LF), 1: R (RF), 2: Ls (LR), 3: Rs (RR), 4: C (CF), matrix
|
||||
* encoded: Cs (CR)
|
||||
*
|
||||
* or: L = left, C = center, R = right, F = front, R = rear
|
||||
*
|
||||
* Filter notation:
|
||||
*
|
||||
* CF
|
||||
* OF AF
|
||||
* Ear->
|
||||
* OR AR
|
||||
* CR
|
||||
*
|
||||
* or: C = center, A = same side, O = opposite, F = front, R = rear
|
||||
*/
|
||||
|
||||
while(in < end) {
|
||||
const int k = s->cyc_pos;
|
||||
|
||||
update_ch(s, in, k);
|
||||
|
||||
/* Simulate a 7.5 ms -20 dB echo of the center channel in the
|
||||
front channels (like reflection from a room wall) - a kind of
|
||||
psycho-acoustically "cheating" to focus the center front
|
||||
channel, which is normally hard to be perceived as front */
|
||||
s->lf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen];
|
||||
s->rf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen];
|
||||
|
||||
switch (s->decode_mode) {
|
||||
case HRTF_MIX_51:
|
||||
case HRTF_MIX_MATRIX2CH:
|
||||
/* Mixer filter matrix */
|
||||
common = conv(dblen, hlen, s->cf, s->cf_ir, k + s->cf_o);
|
||||
if(s->matrix_mode) {
|
||||
/* In matrix decoding mode, the rear channel gain must be
|
||||
renormalized, as there is an additional channel. */
|
||||
matrix_decode(in, k, 2, 3, 0, s->dlbuflen,
|
||||
s->lr_fwr, s->rr_fwr,
|
||||
s->lrprr_fwr, s->lrmrr_fwr,
|
||||
&(s->adapt_lr_gain), &(s->adapt_rr_gain),
|
||||
&(s->adapt_lrprr_gain), &(s->adapt_lrmrr_gain),
|
||||
s->lr, s->rr, NULL, NULL, s->cr);
|
||||
common +=
|
||||
conv(dblen, hlen, s->cr, s->cr_ir, k + s->cr_o) *
|
||||
M1_76DB;
|
||||
left =
|
||||
( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) +
|
||||
conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) +
|
||||
(conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) +
|
||||
conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o)) *
|
||||
M1_76DB + common);
|
||||
right =
|
||||
( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) +
|
||||
conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) +
|
||||
(conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) +
|
||||
conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o)) *
|
||||
M1_76DB + common);
|
||||
} else {
|
||||
left =
|
||||
( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) +
|
||||
conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) +
|
||||
conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) +
|
||||
conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o) +
|
||||
common);
|
||||
right =
|
||||
( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) +
|
||||
conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) +
|
||||
conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) +
|
||||
conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o) +
|
||||
common);
|
||||
}
|
||||
break;
|
||||
case HRTF_MIX_STEREO:
|
||||
left =
|
||||
( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) +
|
||||
conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o));
|
||||
right =
|
||||
( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) +
|
||||
conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o));
|
||||
break;
|
||||
default:
|
||||
/* make gcc happy */
|
||||
left = 0.0;
|
||||
right = 0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Bass compensation for the lower frequency cut of the HRTF. A
|
||||
cross talk of the left and right channel is introduced to
|
||||
match the directional characteristics of higher frequencies.
|
||||
The bass will not have any real 3D perception, but that is
|
||||
OK (note at 180 Hz, the wavelength is about 2 m, and any
|
||||
spatial perception is impossible). */
|
||||
left_b = conv(dblen, blen, s->ba_l, s->ba_ir, k);
|
||||
right_b = conv(dblen, blen, s->ba_r, s->ba_ir, k);
|
||||
left += (1 - BASSCROSS) * left_b + BASSCROSS * right_b;
|
||||
right += (1 - BASSCROSS) * right_b + BASSCROSS * left_b;
|
||||
/* Also mix the LFE channel (if available) */
|
||||
if(data->nch >= 6) {
|
||||
left += in[5] * M3_01DB;
|
||||
right += in[5] * M3_01DB;
|
||||
}
|
||||
|
||||
/* Amplitude renormalization. */
|
||||
left *= AMPLNORM;
|
||||
right *= AMPLNORM;
|
||||
|
||||
switch (s->decode_mode) {
|
||||
case HRTF_MIX_51:
|
||||
case HRTF_MIX_STEREO:
|
||||
/* "Cheating": linear stereo expansion to amplify the 3D
|
||||
perception. Note: Too much will destroy the acoustic space
|
||||
and may even result in headaches. */
|
||||
diff = STEXPAND2 * (left - right);
|
||||
out[0] = av_clip_int16(left + diff);
|
||||
out[1] = av_clip_int16(right - diff);
|
||||
break;
|
||||
case HRTF_MIX_MATRIX2CH:
|
||||
/* Do attempt any stereo expansion with matrix encoded
|
||||
sources. The L, R channels are already stereo expanded
|
||||
by the steering, any further stereo expansion will sound
|
||||
very unnatural. */
|
||||
out[0] = av_clip_int16(left);
|
||||
out[1] = av_clip_int16(right);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Next sample... */
|
||||
in = &in[data->nch];
|
||||
out = &out[af->data->nch];
|
||||
(s->cyc_pos)--;
|
||||
if(s->cyc_pos < 0)
|
||||
s->cyc_pos += dblen;
|
||||
}
|
||||
|
||||
talloc_free(data);
|
||||
af_add_output_frame(af, outframe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int allocate(af_hrtf_t *s)
|
||||
{
|
||||
if ((s->lf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->rf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->lr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->rr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->cf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->cr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->ba_l = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->ba_r = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->fwrbuf_l =
|
||||
malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->fwrbuf_r =
|
||||
malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->fwrbuf_lr =
|
||||
malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
if ((s->fwrbuf_rr =
|
||||
malloc(s->dlbuflen * sizeof(float))) == NULL) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate memory and set function pointers */
|
||||
static int af_open(struct af_instance* af)
|
||||
{
|
||||
int i;
|
||||
af_hrtf_t *s;
|
||||
float fc;
|
||||
|
||||
af->control = control;
|
||||
af->uninit = uninit;
|
||||
af->filter_frame = filter;
|
||||
|
||||
s = af->priv;
|
||||
|
||||
s->dlbuflen = DELAYBUFLEN;
|
||||
s->hrflen = HRTFFILTLEN;
|
||||
s->basslen = BASSFILTLEN;
|
||||
|
||||
s->cyc_pos = s->dlbuflen - 1;
|
||||
/* With a full (two axis) steering matrix decoder, s->matrix_mode
|
||||
should not be enabled lightly (it will also steer the Ls, Rs
|
||||
channels). */
|
||||
s->matrix_mode = 0;
|
||||
s->decode_mode = HRTF_MIX_51;
|
||||
|
||||
switch (s->mode) {
|
||||
case 0: /* Use matrix rear decoding. */
|
||||
s->matrix_mode = 1;
|
||||
break;
|
||||
case 1: /* Input needs matrix decoding. */
|
||||
s->decode_mode = HRTF_MIX_MATRIX2CH;
|
||||
break;
|
||||
case 2:
|
||||
s->matrix_mode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
s->print_flag = 1;
|
||||
|
||||
if (allocate(s) != 0) {
|
||||
MP_ERR(af, "Memory allocation error.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
for(i = 0; i < s->dlbuflen; i++)
|
||||
s->lf[i] = s->rf[i] = s->lr[i] = s->rr[i] = s->cf[i] =
|
||||
s->cr[i] = 0;
|
||||
|
||||
s->lr_fwr =
|
||||
s->rr_fwr = 0;
|
||||
|
||||
s->cf_ir = cf_filt + (s->cf_o = pulse_detect(cf_filt));
|
||||
s->af_ir = af_filt + (s->af_o = pulse_detect(af_filt));
|
||||
s->of_ir = of_filt + (s->of_o = pulse_detect(of_filt));
|
||||
s->ar_ir = ar_filt + (s->ar_o = pulse_detect(ar_filt));
|
||||
s->or_ir = or_filt + (s->or_o = pulse_detect(or_filt));
|
||||
s->cr_ir = cr_filt + (s->cr_o = pulse_detect(cr_filt));
|
||||
|
||||
if((s->ba_ir = malloc(s->basslen * sizeof(float))) == NULL) {
|
||||
MP_ERR(af, "Memory allocation error.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
fc = 2.0 * BASSFILTFREQ / (float)af->data->rate;
|
||||
if(af_filter_design_fir(s->basslen, s->ba_ir, &fc, LP | KAISER, 4 * M_PI) ==
|
||||
-1) {
|
||||
MP_ERR(af, "Unable to design low-pass "
|
||||
"filter.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
for(i = 0; i < s->basslen; i++)
|
||||
s->ba_ir[i] *= BASSGAIN;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_hrtf_t
|
||||
const struct af_info af_info_hrtf = {
|
||||
.info = "HRTF Headphone",
|
||||
.name = "hrtf",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_hrtf_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_CHOICE("mode", mode, 0, ({"m", 0}, {"s", 1}, {"0", 2})),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,510 +0,0 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_AF_HRTF_H
|
||||
#define MPLAYER_AF_HRTF_H
|
||||
|
||||
#define HRTF_MIX_51 0
|
||||
#define HRTF_MIX_STEREO 1
|
||||
#define HRTF_MIX_MATRIX2CH 2
|
||||
|
||||
/* Amplitude scaling factors */
|
||||
#define M17_0DB 0.1414213562
|
||||
#define M9_03DB 0.3535533906
|
||||
#define M6_99DB 0.4472135955
|
||||
#define M4_77DB 0.5773502692
|
||||
#define M3_01DB 0.7071067812
|
||||
#define M1_76DB 0.8164965809
|
||||
|
||||
#define DELAYBUFLEN 1024 /* Length of the delay buffer */
|
||||
#define HRTFFILTLEN 64 /* HRTF filter length */
|
||||
#define IRTHRESH 0.001 /* Impulse response pruning thresh. */
|
||||
|
||||
#define AMPLNORM M6_99DB /* Overall amplitude renormalization */
|
||||
|
||||
#define BASSFILTFREQ 180 /* Bass compensation filter cut (Hz) */
|
||||
#define BASSFILTLEN 193 /* Bass compensation filter length */
|
||||
#define BASSGAIN M_SQRT2 /* Bass compensation gain */
|
||||
#define BASSCROSS 0.35 /* Bass cross talk */
|
||||
|
||||
#define FWRDURATION 240 /* FWR average duration (samples) */
|
||||
#define MATREARDELAY 720 /* Matrix mode rear delay (samples) */
|
||||
|
||||
#define MATAGCTRIG 8.0 /* (Fuzzy) AGC trigger */
|
||||
#define MATAGCDECAY 1.0 /* AGC baseline decay rate (1/samp.) */
|
||||
#define MATAGCLOCK 0.2 /* AGC range (around 1) where the
|
||||
matrix behaves passively */
|
||||
#define MATCOMPGAIN 0.37 /* Cross talk compensation gain,
|
||||
0.50 - 0.55 is full cancellation. */
|
||||
|
||||
#define CFECHODELAY 360 /* Center front echo delay (samples) */
|
||||
#define CFECHOAMPL M17_0DB /* Center front echo amplitude */
|
||||
|
||||
#define STEXPAND2 0.07 /* Stereo expansion / 2 */
|
||||
|
||||
/* Head related impulse response (HRIR) derived from KEMAR measurement
|
||||
data by Bill Gardner <billg@media.mit.edu> and Keith Martin
|
||||
<kdm@media.mit.edu>
|
||||
|
||||
URL: http://sound.media.mit.edu/KEMAR.html
|
||||
|
||||
Distributed under GPL with authors' permission
|
||||
*/
|
||||
|
||||
/* EQUALIZED KEMAR HRIR
|
||||
|
||||
How to generate these data:
|
||||
|
||||
1. You need the MIT Media Lab's KEMAR data, read it into an software
|
||||
capable of signal/time series analysis (like Mathematica, Matlab,
|
||||
...)
|
||||
|
||||
2. Construct an equalizing FIR inverse filter by calculating the
|
||||
transfer function of the front, same side impulse response, then
|
||||
take 1 over the absolute magnitude.
|
||||
|
||||
3. Cut the poles in the inverse filter's transfer function
|
||||
specification by limiting the amplification to 2.5 (note, this
|
||||
number assumes that you have correct signal processing
|
||||
normalization of the Fourier transform).
|
||||
|
||||
4. Design the FIR inverse filter by calculating the inverse Fourier
|
||||
transform, then chopping the coefficients down to a reasonable
|
||||
number (N = 15 is used here), apply the Kaiser-Bessel window (alpha
|
||||
= 2 is used here). Note the objective is remove the color bias
|
||||
only (as if you are using an equalizer), _not_ to do a full inverse
|
||||
filtering. (Note: beta = pi*alpha in other notation.)
|
||||
|
||||
For N = 15, alpha = 2, you should get the following impulse
|
||||
response:
|
||||
|
||||
0.001001558668605168, 0.00698457265741865, 0.040453643039829436,
|
||||
0.012230541722147855, -0.11939760844854072, 0.16468099899755967,
|
||||
-0.30297563073747436, 1.3140211791355982, -0.30297563073747436,
|
||||
0.16468099899755967, -0.11939760844854072, 0.012230541722147855,
|
||||
0.040453643039829436, 0.00698457265741865, 0.001001558668605168
|
||||
|
||||
5. Linearly convolve all KEMAR HRIR with this inverse filter.
|
||||
|
||||
6. Resample from 44.1 kHz sampling frequency to 48 kHz.
|
||||
*/
|
||||
|
||||
/* Center front (-5 degree) - not 0 degree in order to create a clear
|
||||
front image from a finite distance */
|
||||
static const float cf_filt[128] = {
|
||||
-0.00008638082319075036, 0.0003198059946385229,
|
||||
-0.0005010631339162132, 0.0011424741331126876,
|
||||
-0.001584220794688753, 0.001742715363246275,
|
||||
-0.0011080796626780694, 0.0001651829990860167,
|
||||
0.005235028337314985, 0.0035223828473357776,
|
||||
0.010057681388431303, -0.033469432129545514,
|
||||
0.013391253316233523, 0.004858462839827063,
|
||||
0.08172161220103627, 0.26158596134500023,
|
||||
-0.12420314583323326, -0.14298458356097565,
|
||||
0.14421897280453896, -0.1635792507629016,
|
||||
-0.02187136722480014, 0.2426863044711817,
|
||||
0.07229814207917194, 0.0942742651913879,
|
||||
0.29856830878076834, 0.2944146162057754,
|
||||
-0.12122157003421209, -0.19640092165631157,
|
||||
0.11623836502034968, -0.05794027397995521,
|
||||
-0.34313138341973776, -0.19487516249168105,
|
||||
0.010118993953802401, -0.09460218797710966,
|
||||
-0.16761521117359582, 0.004154461610153861,
|
||||
0.052768641758969316, -0.00041823982226147407,
|
||||
0.021634960445143514, 0.07562793486871108,
|
||||
0.08895407129506479, 0.039857755093416214,
|
||||
0.044257936180422945, 0.061557584906101664,
|
||||
0.015547268541895703, -0.023908191934932484,
|
||||
0.009498030443468223, 0.03816269003221337,
|
||||
-0.009820500607303615, -0.042003975527908084,
|
||||
-0.03335447117311547, -0.029294510859746596,
|
||||
-0.05212623136198511, -0.073427547153205,
|
||||
-0.061190797824958836, -0.04620925059966413,
|
||||
-0.04204619420702159, -0.02331915902615157,
|
||||
0.00211481411477094, 0.00852563995740107,
|
||||
0.008766809731743399, 0.008666632180812078,
|
||||
0.018279202191625352, 0.02924332751289675,
|
||||
0.022293148257836494, 0.012362146008584188,
|
||||
0.008572582458807008, 0.006491370763597344,
|
||||
-0.0019366944997535774, -0.006318669309634434,
|
||||
-0.006457921690218669, -0.015050265524669974,
|
||||
-0.02110660282616213, -0.017027809096377904,
|
||||
-0.01651052305334348, -0.022770064150046673,
|
||||
-0.01999875754219472, -0.012294792027337775,
|
||||
-0.011506057031057188, -0.011448970577312903,
|
||||
-0.004823572302580925, 0.0022451134042777883,
|
||||
0.004145473526859826, 0.005629030064546135,
|
||||
0.008588029213398785, 0.010092048834844231,
|
||||
0.007182013245552008, 0.0014600979508720656,
|
||||
-0.0038314646272511756, -0.003443901997881347,
|
||||
-0.0029483418254804047, -0.007609357112679647,
|
||||
-0.006518368948030822, -0.004495803701497202,
|
||||
-0.007109113004849672, -0.008346237278084265,
|
||||
-0.005560847336252453, -0.002993453167553188,
|
||||
-0.005122897816824269, -0.004389782626604215,
|
||||
-0.0010912633695218108, -0.0019712029474458835,
|
||||
-0.005870162265802235, -0.005626159534954128,
|
||||
-0.00027254977910844407, 0.0013794425431633785,
|
||||
-0.0005919083190430062, -0.0007861203545416682,
|
||||
-0.0007049560240893946, -0.0032720188494468868,
|
||||
-0.004460645567968504, -0.0032018528193571696,
|
||||
-0.0030579229375062105, -0.003593998902656612,
|
||||
-0.0038032977997776445, -0.004613776010454773,
|
||||
-0.0059796549143736845, -0.00420126194319768,
|
||||
-0.0012374419948287222, 0.0008572699213050608,
|
||||
0.0021490971020081094, 0.00047295283198381995,
|
||||
-0.0009670277915884887, -0.001354440866080231,
|
||||
-0.002962902746547851, -0.00533935813338141,
|
||||
-0.005469203016468759, -0.004355784273189485
|
||||
};
|
||||
/* Front, same side (30 degree) */
|
||||
static const float af_filt[128] = {
|
||||
-0.004140580614755493, 0.005790934614385445,
|
||||
0.003318916682081112, 0.014257145544366063,
|
||||
0.007328442487127339, -0.06550381777876194,
|
||||
0.03502225818161845, -0.013090579770708259,
|
||||
0.2517776798694195, 0.420770489950659,
|
||||
-0.3418854608834852, -0.24371032493696737,
|
||||
0.04901356150030018, -0.1778083521632833,
|
||||
0.26448004245714163, 0.23245199964546834,
|
||||
0.033053145803936305, 0.46811222821062415,
|
||||
0.5359265986255611, -0.011912195468533787,
|
||||
-0.39763432601411647, -0.034482864386898314,
|
||||
0.029445398240649626, -0.3850940407863262,
|
||||
-0.3272328478175581, -0.14701421403616477,
|
||||
-0.08522137400169517, -0.14936851633336035,
|
||||
-0.09432605283433723, 0.0991200405937827,
|
||||
0.011075012089917331, -0.0051036489980274685,
|
||||
0.0933903289749412, 0.1344189369609565,
|
||||
0.10507466913017807, 0.04240159324684365,
|
||||
0.06559270110638656, 0.026984119875617524,
|
||||
-0.03359846103238096, -0.018000197099174275,
|
||||
0.042031818548178244, 0.03849039666888434,
|
||||
-0.02450829674011345, -0.03407882403088576,
|
||||
-0.029230189282961977, -0.046964865291761734,
|
||||
-0.09458258700116245, -0.1527349330901158,
|
||||
-0.15411577687826097, -0.08761679790956928,
|
||||
-0.033623549089171874, -0.007204768531481949,
|
||||
0.008273425020444852, 0.021368717994908505,
|
||||
0.04366608267875025, 0.05660907333076205,
|
||||
0.06775726495503939, 0.05621881735271431,
|
||||
0.03576231950669927, 0.02500825834889175,
|
||||
0.015423811076054601, 0.007903258334503761,
|
||||
-0.0053873014137761945, -0.006987955469434698,
|
||||
-0.012027972007598602, -0.025228281243816594,
|
||||
-0.026225091797257318, -0.023809293997344882,
|
||||
-0.03250172017712295, -0.03195696301067249,
|
||||
-0.01784813952189948, -0.01663267233760342,
|
||||
-0.016184530450468065, -0.011659883749357463,
|
||||
-0.0035378511240219163, -0.0005485800790443406,
|
||||
0.0018432660108168625, 0.011634844139907534,
|
||||
0.018333603402051105, 0.020447379185133056,
|
||||
0.00850783664147828, 0.0004694148911037838,
|
||||
-0.0017047130409786676, -0.0022409152834483997,
|
||||
-0.000860472174892845, -0.004111075059198666,
|
||||
-0.003527843382056666, -0.009640160874903018,
|
||||
-0.01750044574231376, -0.015613389403672443,
|
||||
-0.010790028120953001, -0.0095313499955768,
|
||||
-0.007469721416726809, -0.0019186578145117315,
|
||||
-0.00014977322572890802, -0.0029803838028179728,
|
||||
-0.006520567233727221, 0.000035015132033882596,
|
||||
0.009245098100543752, 0.009896930052308656,
|
||||
0.008316744929565786, 0.004575207140193997,
|
||||
-0.0000647420103997081, -0.004502916832871627,
|
||||
-0.004225962213251224, -0.002886014126381486,
|
||||
-0.006416834142585976, -0.007156609995423569,
|
||||
-0.008840274447579472, -0.01441763751386817,
|
||||
-0.015435817484659574, -0.00924487254924743,
|
||||
-0.0021571721940235205, 0.0028540722992305453,
|
||||
0.00273577475088536, -0.000788412365513805,
|
||||
-0.0032650029728365907, -0.003880217646231338,
|
||||
-0.0035302087299613778, -0.0038531436176586246,
|
||||
-0.0011921632190514074, -0.0020722967099011938,
|
||||
-0.004985351145629344, -0.0042375588844648735,
|
||||
-0.003030360463006021, -0.0014161075428041471,
|
||||
-0.0005083025643192044, 0.00035096963769606926
|
||||
};
|
||||
/* Front, opposite (-30 degree) */
|
||||
static const float of_filt[128] = {
|
||||
-0.000013472538374193126, -0.00008048061877079751,
|
||||
0.000043927265781258155, -0.000017931700794858892,
|
||||
-0.000034774602476112886, -0.00009576223008735474,
|
||||
0.0001557797638630691, -0.00018742885883751094,
|
||||
0.00026512448626705716, -0.0001451040203319678,
|
||||
-0.00008263233117758043, 0.0006486245853639179,
|
||||
-0.0010631408451846698, 0.0026571994100746143,
|
||||
0.0014179177997092787, 0.0062326502956616256,
|
||||
-0.008194149324545333, -0.006568029415878379,
|
||||
0.009538759710818582, 0.012309193558632693,
|
||||
0.12336638055838955, 0.046164307101829005,
|
||||
-0.10228706407884815, 0.04047687260345798,
|
||||
-0.00296595313977046, -0.07057949208414134,
|
||||
0.08172114840714612, 0.08736490764127891,
|
||||
0.05105250431333021, 0.11627179512747428,
|
||||
0.20717888490340705, 0.09375052213570291,
|
||||
-0.09784374168330194, -0.010493571845901443,
|
||||
0.053131894303891716, -0.10157443971694806,
|
||||
-0.16264032634244974, -0.05402369511361273,
|
||||
-0.0274403608654217, -0.09860277022495063,
|
||||
-0.06841875821090282, -0.004434574400066223,
|
||||
-0.0005222661652743502, -0.006231881259827263,
|
||||
0.014410397820340159, 0.04885649512730243,
|
||||
0.04361962569042684, 0.03399214029009391,
|
||||
0.04961073933475931, 0.04067325604132289,
|
||||
0.007850647519227257, 0.004564440466905299,
|
||||
0.02257107958021618, 0.008183791928884486,
|
||||
-0.014913479343180557, -0.018685938460856224,
|
||||
-0.01745737397226911, -0.02327177054233603,
|
||||
-0.03723048632685227, -0.044739390162299685,
|
||||
-0.042651220125613766, -0.03730017561004743,
|
||||
-0.029039465434276192, -0.01885087458914294,
|
||||
-0.01207127752277769, -0.006779800724164512,
|
||||
-0.001930416967444157, 0.000029454577995528385,
|
||||
0.0013822760965755472, 0.0014799128583230202,
|
||||
0.0002068200609199832, 0.0022254295286201083,
|
||||
0.005143858159434566, 0.0018580542060917013,
|
||||
-0.0019426046325146259, -0.0014464042108543495,
|
||||
-0.0034430083560735582, -0.009692758426099499,
|
||||
-0.011840035292593485, -0.010716508855893968,
|
||||
-0.012939889036853034, -0.0121846427935653,
|
||||
-0.006198503315630782, -0.0023186723099380305,
|
||||
-0.002679872498314837, -0.003086020446226295,
|
||||
-0.0015709623347698936, -0.0008147490468332398,
|
||||
-0.0012384575726770983, -0.0005212877089109362,
|
||||
0.0017707578744906142, 0.001324932723905786,
|
||||
-0.0017023653780617696, -0.0045108927752919425,
|
||||
-0.005422155613096912, -0.0039489323837623835,
|
||||
-0.005295995750506547, -0.00629706566356189,
|
||||
-0.004685732198036754, -0.0048076735568143,
|
||||
-0.005978864279217503, -0.005928999306332966,
|
||||
-0.004187703549017582, -0.003213999896976475,
|
||||
-0.0028068699816073414, -0.0010889703907593833,
|
||||
0.0003276714243495386, -0.0013015007040186994,
|
||||
-0.003208050402434782, -0.0025115319088208545,
|
||||
-0.0013787553006401076, -0.0018279087370218635,
|
||||
-0.0025904836507747754, -0.002071221947222004,
|
||||
-0.0026424212922485594, -0.0039837031817577,
|
||||
-0.0041635566057380965, -0.004355223489150822,
|
||||
-0.004350395332709937, -0.0036693292471930263,
|
||||
-0.003386384394185026, -0.003972568655001128,
|
||||
-0.004332336840023821, -0.002648767912111827,
|
||||
-0.001384410080218114, -0.0011353792711849466,
|
||||
-0.0013726264946164232, -0.0020075119315034313
|
||||
};
|
||||
/* Rear, same side (135 degree) */
|
||||
static const float ar_filt[128] = {
|
||||
0.004573315040810066, 0.0013592578059426913,
|
||||
0.01553271930902704, -0.0002356117224941317,
|
||||
-0.05746098219774702, 0.03430688963370445,
|
||||
0.00808371687447385, 0.21893535841158596,
|
||||
0.2984357591724814, -0.3302799746504719,
|
||||
-0.3194029149806245, 0.21633225051331056,
|
||||
0.24371260938097083, -0.08843705549751085,
|
||||
0.03939684701343366, 0.45386926431114494,
|
||||
0.07599118140753386, -0.18114706160474578,
|
||||
0.285640624686038, 0.4049515236666218,
|
||||
-0.05347890222071792, -0.31464359045319074,
|
||||
-0.1033502246468194, -0.04553593949283157,
|
||||
-0.1880747731157464, -0.13629090230626037,
|
||||
-0.10435789106123239, -0.19818232801888755,
|
||||
-0.16701805476330397, -0.022793111199284,
|
||||
0.058049696762683685, 0.007048321372693906,
|
||||
-0.002966419183225961, 0.10140569697797763,
|
||||
0.11648999956673124, 0.05218347182779882,
|
||||
0.028427001212735392, 0.04151900310166159,
|
||||
-0.0006960604221423734, -0.05898623212226975,
|
||||
-0.03801934531173312, -0.029306970535287986,
|
||||
-0.04549125782835908, -0.0599222718506552,
|
||||
-0.058299618975430116, -0.03765579129720727,
|
||||
-0.03559302657499581, -0.020647901025903054,
|
||||
-0.005720957338744348, -0.0041915732688915545,
|
||||
-0.0011470880098346143, 0.008737404798553,
|
||||
0.023444168098121512, 0.024204226042172663,
|
||||
0.01894897166475026, 0.020807655257479588,
|
||||
0.021570431128040954, 0.006800556178576289,
|
||||
-0.009000089216921362, -0.010969824547067934,
|
||||
-0.0033653428332822374, -0.012676936164668659,
|
||||
-0.026739938673413587, -0.023427869194287573,
|
||||
-0.023302007105117244, -0.023647155590533712,
|
||||
-0.021289317515613106, -0.009120487305069884,
|
||||
0.0009251551667728967, 0.00004285344125653763,
|
||||
-0.00009042365479456271, 0.00022573242339446494,
|
||||
0.00720168491586098, 0.007111875505402431,
|
||||
0.003186514817683482, 0.00810087718334745,
|
||||
0.012619557025922575, 0.007854726400013397,
|
||||
-0.0024013592881066267, -0.001452457473161119,
|
||||
-0.0025535188366093945, -0.012428911627809337,
|
||||
-0.013729251536694145, -0.0070099675146427344,
|
||||
-0.007165284278706593, -0.01639289295622301,
|
||||
-0.015831795079778305, -0.007305768485523729,
|
||||
-0.003608863157004021, -0.0032640528878698084,
|
||||
0.0030901263998481944, 0.00749497566124848,
|
||||
0.002515185532327241, 0.00004840875738621367,
|
||||
0.0017596043486043966, 0.0046229941553338144,
|
||||
0.0034259167322926096, 0.003707347634186093,
|
||||
0.0035584806528586328, -0.0019078936035275198,
|
||||
-0.006057343815214898, -0.0069262470468817,
|
||||
-0.004345020728618624, -0.004177623355574794,
|
||||
-0.005373506556122508, -0.006624933928893836,
|
||||
-0.008679541408588839, -0.010718719681595322,
|
||||
-0.011392246979594496, -0.007893917064389902,
|
||||
-0.0027572935365832536, -0.00006064707149834412,
|
||||
-0.0012512537319656323, -0.0024501501002409786,
|
||||
-0.0022106788572895998, -0.00234124933370301,
|
||||
-0.0008953445167066823, 0.0005393670625637734,
|
||||
-0.00033175600142209297, -0.004023994309351289,
|
||||
-0.008655472335784443, -0.009899957354849682,
|
||||
-0.008664952919996412, -0.00553483124503576,
|
||||
-0.003735336089277662, -0.002754824348643885,
|
||||
-0.0026884314856593368, -0.004084181815125924
|
||||
};
|
||||
/* Rear, opposite (-135 degree) */
|
||||
static const float or_filt[128] = {
|
||||
0.0001220944028243897, -0.000021785381808441314,
|
||||
5.823057988603169e-6, -0.00001217768176447613,
|
||||
-0.00006123604397345513, 5.574117262531134e-6,
|
||||
-0.00004935331914366778, 1.1771577934768211e-6,
|
||||
-0.000059236211621095756, 9.503536190497286e-6,
|
||||
-0.0001494445696103564, 0.00012248858284145305,
|
||||
-0.0000963975321456313, 6.017905197665205e-6,
|
||||
0.00003353395360402643, -0.0001931511015359506,
|
||||
0.0005113536523931485, -0.0005676652619386114,
|
||||
0.0012057159755477467, 0.0009370492250339692,
|
||||
0.004596472288877981, -0.0018831773384237068,
|
||||
-0.008208535225621212, 0.0038178646400751056,
|
||||
0.008726517739105965, 0.06664363898418262,
|
||||
0.06788684221502142, -0.04492315162807267,
|
||||
-0.04019906311255255, 0.026203059677375153,
|
||||
0.013678129114847544, -0.014334962223993527,
|
||||
0.010141709596167392, 0.11559131576945537,
|
||||
0.1586402064538425, 0.059975334707967023,
|
||||
0.004671725963777715, 0.031498678282775874,
|
||||
0.014338626006524587, -0.014749719448472231,
|
||||
-0.02055508237941379, -0.05407690143992048,
|
||||
-0.07767559836886143, -0.05029091786216801,
|
||||
-0.030808335706574427, -0.03401958135442541,
|
||||
-0.030520368430288967, -0.014168302104259355,
|
||||
0.011907621259989802, 0.014286081013069,
|
||||
0.006377467879613449, 0.018546823568277478,
|
||||
0.028297012771618273, 0.025222339408338186,
|
||||
0.021931611353415138, 0.019708894333646355,
|
||||
0.01729258494072014, 0.017468204169564034,
|
||||
0.009729094845051928, -0.002976992018531901,
|
||||
-0.00956986166277019, -0.016125733548332074,
|
||||
-0.02934094241442545, -0.04133767871051455,
|
||||
-0.043536981145416466, -0.0385966307108608,
|
||||
-0.02784453599342459, -0.018995135307247116,
|
||||
-0.012849534096536747, -0.004437491064613308,
|
||||
0.00028385411598204655, 0.003776874988516643,
|
||||
0.008069432041547833, 0.008764754183751848,
|
||||
0.008020908861878062, 0.006830351461360802,
|
||||
0.002218330884267235, -0.0020478725582339444,
|
||||
-0.003997428121462543, -0.007066287373515421,
|
||||
-0.00940847412544698, -0.010938998446237963,
|
||||
-0.011775483016151306, -0.011391103919484287,
|
||||
-0.010586061195163017, -0.009842793078929053,
|
||||
-0.007753202010139829, -0.00569213732353025,
|
||||
-0.006506783349722073, -0.005346134281903736,
|
||||
-0.003913089814898934, -0.0036091443854759727,
|
||||
-0.0020328564301266287, 0.00017932870773467683,
|
||||
0.0032779786679056357, 0.003969695813293966,
|
||||
0.0020339334412434987, -0.00011345940675415259,
|
||||
-0.0018344103399567666, -0.003556764701666365,
|
||||
-0.004263523639408391, -0.002940568582022133,
|
||||
-0.0034341188272627556, -0.006023399920020824,
|
||||
-0.0077456903203677865, -0.007912219312377842,
|
||||
-0.00625202770436523, -0.00530785086116117,
|
||||
-0.005569722659634311, -0.004664448462594344,
|
||||
-0.0037747773914077747, -0.004175649656985592,
|
||||
-0.004659601521384289, -0.005008602967819641,
|
||||
-0.004730625079902729, -0.0034039554356604146,
|
||||
-0.0017110333873406587, -0.0006091938771510242,
|
||||
-0.0016051679050678297, -0.003312864664007262,
|
||||
-0.004505512715977288, -0.004152222189861692,
|
||||
-0.003218596419678823, -0.0027277806209877343,
|
||||
-0.001715005444317267, -0.0012589960071233749,
|
||||
-0.001852908777923165, -0.002540339553144362
|
||||
};
|
||||
/* Center rear (180 degree) */
|
||||
static const float cr_filt[128] = {
|
||||
-0.00005989110716536726, -0.00022790291829128702,
|
||||
0.0002659166098971966, -0.0003774772716776257,
|
||||
0.0004540309551867803, -0.000420238187386368,
|
||||
0.00025518536450885686, 0.00028285526288953955,
|
||||
-0.001016391007574093, 0.0028634984299063795,
|
||||
0.0021574799687976045, 0.01035121276682072,
|
||||
-0.010481720917298163, -0.013197198495899292,
|
||||
0.0031928225328717195, 0.02903137843618603,
|
||||
0.1632429772511569, 0.1047487989875262,
|
||||
-0.10464685060623742, -0.09260196288035998,
|
||||
-0.007514241993554443, 0.013596249226741712,
|
||||
-0.019876166508450258, 0.1255626123599804,
|
||||
0.3648170359521724, 0.19458249753223478,
|
||||
-0.04434070930031298, 0.046582528121935265,
|
||||
0.09484427347230277, -0.03137795311969644,
|
||||
-0.10297437925363695, -0.09351091015917065,
|
||||
-0.1129521092162064, -0.14925322995658827,
|
||||
-0.1231466295584665, -0.06356719756705227,
|
||||
-0.05442277895126282, -0.07568433015661316,
|
||||
-0.023314932828602003, 0.04260950721318558,
|
||||
0.02249026315598923, 0.02048195669571197,
|
||||
0.05651342117268278, 0.05885038917623213,
|
||||
0.03797102097397795, 0.011767394419953451,
|
||||
0.00560502503429481, 0.005051125343961189,
|
||||
-0.012925933188033823, -0.023918884651306566,
|
||||
-0.013251659441678816, -0.010694772488866284,
|
||||
-0.03080486448617846, -0.03661278237783158,
|
||||
-0.0379227303416262, -0.042189005718490775,
|
||||
-0.026595666344596286, -0.009759025956801257,
|
||||
-0.002064986663513004, -0.002420117028098389,
|
||||
-0.006629991977552491, 0.004619970897631026,
|
||||
0.019450642967537877, 0.0173521119057514,
|
||||
0.017641425439988062, 0.02270029598048491,
|
||||
0.018976431925275348, 0.009299852902290885,
|
||||
-0.001695039371619912, -0.00675162574265618,
|
||||
-0.009380968871003034, -0.011208396125485165,
|
||||
-0.01308640049201482, -0.0165636375633249,
|
||||
-0.022004099870933345, -0.025173458684139286,
|
||||
-0.016918759559175375, -0.00865150653575917,
|
||||
-0.006999929082792643, -0.005454830010518988,
|
||||
-0.0021129521131095317, 0.00018717090054046307,
|
||||
-0.0002864344788569993, 0.0017615225381095569,
|
||||
0.006985907557802283, 0.010577308310476465,
|
||||
0.006466104789306027, -0.0014988738575948326,
|
||||
-0.0039669755229277195, -0.0065156971200080235,
|
||||
-0.009343206924192169, -0.0076430644693577495,
|
||||
-0.004395214976600924, -0.003052735340422483,
|
||||
-0.007019103043066595, -0.00974109267696527,
|
||||
-0.007968015032797376, -0.007801513845528344,
|
||||
-0.007535748903681969, -0.003543341967287925,
|
||||
0.0015083125553729722, 0.0023345972556147025,
|
||||
-0.0010043623069557037, -0.0025295765105203746,
|
||||
-0.0023701840891643634, -0.0005908186035024362,
|
||||
0.0029826252289082847, 0.004829048542117764,
|
||||
0.004488360022902081, 0.00002643748103005408,
|
||||
-0.0042100779212597295, -0.006170600558114495,
|
||||
-0.007267149164680168, -0.006825522903494639,
|
||||
-0.006899834372739123, -0.0073493916110062675,
|
||||
-0.009554351265163382, -0.011790297433830197,
|
||||
-0.010645796603734424, -0.0064661575394022106,
|
||||
-0.002026743466524137, -0.0004337034584909932,
|
||||
-0.0011172647031654614, -0.0017947816283674731,
|
||||
-0.00255615052036616, -0.0017721562881944813,
|
||||
-0.0002379619297227554, 0.0007130120121089036
|
||||
};
|
||||
|
||||
#endif /* MPLAYER_AF_HRTF_H */
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* simple voice removal filter
|
||||
*
|
||||
* copyright (c) 2006 Reynaldo H. Verdejo Pinochet
|
||||
* Based on code by Alex Beregszaszi for his 'center' filter.
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "af.h"
|
||||
|
||||
// Data for specific instances of this filter
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *c)
|
||||
{
|
||||
if (!c)
|
||||
return 0;
|
||||
if (af_make_writeable(af, c) < 0) {
|
||||
talloc_free(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
float* a = c->planes[0]; // Audio data
|
||||
int nch = c->nch; // Number of channels
|
||||
int len = c->samples*nch; // Number of samples in current audio block
|
||||
register int i;
|
||||
|
||||
/*
|
||||
FIXME1 add a low band pass filter to avoid suppressing
|
||||
centered bass/drums
|
||||
FIXME2 better calculated* attenuation factor
|
||||
*/
|
||||
|
||||
for(i=0;i<len;i+=nch)
|
||||
{
|
||||
a[i] = (a[i] - a[i+1]) * 0.7;
|
||||
a[i+1]=a[i];
|
||||
}
|
||||
|
||||
af_add_output_frame(af, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control = control;
|
||||
af->filter_frame = filter_frame;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
// Description of this filter
|
||||
const struct af_info af_info_karaoke = {
|
||||
.info = "Simple karaoke/voice-removal audio filter",
|
||||
.name = "karaoke",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
};
|
||||
@@ -1,851 +0,0 @@
|
||||
/*
|
||||
* LADSPA plugin loader
|
||||
*
|
||||
* Written by Ivo van Poorten <ivop@euronet.nl>
|
||||
* Copyright (C) 2004, 2005
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Global Includes */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <ladspa.h>
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Local Includes */
|
||||
|
||||
#include "af.h"
|
||||
|
||||
#define _(x) (x)
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Filter specific data */
|
||||
|
||||
typedef struct af_ladspa_s
|
||||
{
|
||||
int status; /**< Status of the filter.
|
||||
* Either AF_OK or AF_ERROR
|
||||
* Because MPlayer re-inits audio filters that
|
||||
* _clearly_ returned AF_ERROR anyway, I use this
|
||||
* in play() to skip the processing and return
|
||||
* the data unchanged.
|
||||
*/
|
||||
|
||||
int activated; /**< 0 or 1. Activate LADSPA filters only once, even
|
||||
* if the buffers get resized, to avoid a stuttering
|
||||
* filter.
|
||||
*/
|
||||
|
||||
char *file;
|
||||
char *label;
|
||||
char *controls;
|
||||
|
||||
char *myname; /**< It's easy to have a concatenation of file and label */
|
||||
|
||||
void *libhandle;
|
||||
const LADSPA_Descriptor *plugin_descriptor;
|
||||
|
||||
int nports;
|
||||
|
||||
int ninputs;
|
||||
int *inputs;
|
||||
|
||||
int noutputs;
|
||||
int *outputs;
|
||||
|
||||
int ninputcontrols;
|
||||
int *inputcontrolsmap; /**< Map input port number [0-] to actual port */
|
||||
float *inputcontrols;
|
||||
|
||||
int noutputcontrols;
|
||||
int *outputcontrolsmap;
|
||||
float *outputcontrols;
|
||||
|
||||
int nch; /**< number of channels */
|
||||
int bufsize;
|
||||
float **inbufs;
|
||||
float **outbufs;
|
||||
LADSPA_Handle *chhandles;
|
||||
|
||||
} af_ladspa_t;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static int af_open(struct af_instance *af);
|
||||
static int af_ladspa_malloc_failed(char*);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#define OPT_BASE_STRUCT af_ladspa_t
|
||||
const struct af_info af_info_ladspa = {
|
||||
.info = "LADSPA plugin loader",
|
||||
.name = "ladspa",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_ladspa_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("file", file, 0),
|
||||
OPT_STRING("label", label, 0),
|
||||
OPT_STRING("controls", controls, 0),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* By lack of a better word (in my vocabulary) this is called 'parse'.
|
||||
* Feel free to suggest an alternative.
|
||||
*/
|
||||
|
||||
/** \brief Check for inputs, outputs and controls of a given filter.
|
||||
*
|
||||
* This function counts and checks all input, output and control ports
|
||||
* of the filter that was loaded. If it turns out to be a valid
|
||||
* filter for MPlayer use, it prints out a list of all controls and
|
||||
* the corresponding range of its value at message level MSGL_V.
|
||||
*
|
||||
* \param setup Current setup of the filter. Must have its
|
||||
* plugin_descriptor set!
|
||||
*
|
||||
* \return Returns AF_OK if it has a valid input/output/controls
|
||||
* configuration. Else, it returns AF_ERROR.
|
||||
*/
|
||||
|
||||
static int af_ladspa_parse_plugin(struct af_instance *af) {
|
||||
af_ladspa_t *setup = af->priv;
|
||||
int p, i;
|
||||
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
|
||||
LADSPA_PortDescriptor d;
|
||||
LADSPA_PortRangeHint hint;
|
||||
|
||||
if (!setup->libhandle)
|
||||
return AF_ERROR; /* only call parse after a successful load */
|
||||
if (!setup->plugin_descriptor)
|
||||
return AF_ERROR; /* same as above */
|
||||
|
||||
/* let's do it */
|
||||
|
||||
setup->nports = pdes->PortCount;
|
||||
|
||||
/* allocate memory for all inputs/outputs/controls */
|
||||
|
||||
setup->inputs = calloc(setup->nports, sizeof(int));
|
||||
if (!setup->inputs) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
setup->outputs = calloc(setup->nports, sizeof(int));
|
||||
if (!setup->outputs) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
setup->inputcontrolsmap = calloc(setup->nports, sizeof(int));
|
||||
if (!setup->inputcontrolsmap) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
setup->inputcontrols = calloc(setup->nports, sizeof(float));
|
||||
if (!setup->inputcontrols) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
setup->outputcontrolsmap = calloc(setup->nports, sizeof(int));
|
||||
if (!setup->outputcontrolsmap) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
setup->outputcontrols = calloc(setup->nports, sizeof(float));
|
||||
if (!setup->outputcontrols) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
/* set counts to zero */
|
||||
|
||||
setup->ninputs = 0;
|
||||
setup->noutputs = 0;
|
||||
setup->ninputcontrols = 0;
|
||||
setup->noutputcontrols = 0;
|
||||
|
||||
/* check all ports, see what type it is and set variables according to
|
||||
* what we have found
|
||||
*/
|
||||
|
||||
for (p=0; p<setup->nports; p++) {
|
||||
d = pdes->PortDescriptors[p];
|
||||
|
||||
if (LADSPA_IS_PORT_AUDIO(d)) {
|
||||
if (LADSPA_IS_PORT_INPUT(d)) {
|
||||
setup->inputs[setup->ninputs] = p;
|
||||
setup->ninputs++;
|
||||
} else if (LADSPA_IS_PORT_OUTPUT(d)) {
|
||||
setup->outputs[setup->noutputs] = p;
|
||||
setup->noutputs++;
|
||||
}
|
||||
}
|
||||
|
||||
if (LADSPA_IS_PORT_CONTROL(d)) {
|
||||
if (LADSPA_IS_PORT_INPUT(d)) {
|
||||
setup->inputcontrolsmap[setup->ninputcontrols] = p;
|
||||
setup->ninputcontrols++;
|
||||
/* set control to zero. set values after reading the rest
|
||||
* of the suboptions and check LADSPA_?_HINT's later.
|
||||
*/
|
||||
setup->inputcontrols[p] = 0.0f;
|
||||
} else if (LADSPA_IS_PORT_OUTPUT(d)) {
|
||||
/* read and handle these too, otherwise filters that have them
|
||||
* will sig11
|
||||
*/
|
||||
setup->outputcontrolsmap[setup->noutputcontrols]=p;
|
||||
setup->noutputcontrols++;
|
||||
setup->outputcontrols[p] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (setup->ninputs == 0) {
|
||||
MP_WARN(af, "%s: %s\n", setup->myname,
|
||||
_("WARNING! This LADSPA plugin has no audio inputs.\n The incoming audio signal will be lost."));
|
||||
} else if (setup->ninputs == 1) {
|
||||
MP_VERBOSE(af, "%s: this is a mono effect\n", setup->myname);
|
||||
} else if (setup->ninputs == 2) {
|
||||
MP_VERBOSE(af, "%s: this is a stereo effect\n", setup->myname);
|
||||
} else {
|
||||
MP_VERBOSE(af, "%s: this is a %i-channel effect, "
|
||||
"support is experimental\n", setup->myname, setup->ninputs);
|
||||
}
|
||||
|
||||
if (setup->noutputs == 0) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("This LADSPA plugin has no audio outputs."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
if (setup->noutputs != setup->ninputs ) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("The number of audio inputs and audio outputs of the LADSPA plugin differ."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
MP_VERBOSE(af, "%s: this plugin has %d input control(s)\n",
|
||||
setup->myname, setup->ninputcontrols);
|
||||
|
||||
/* Print list of controls and its range of values it accepts */
|
||||
|
||||
for (i=0; i<setup->ninputcontrols; i++) {
|
||||
p = setup->inputcontrolsmap[i];
|
||||
hint = pdes->PortRangeHints[p];
|
||||
MP_VERBOSE(af, " --- %d %s [", i, pdes->PortNames[p]);
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
|
||||
MP_VERBOSE(af, "%0.2f , ", hint.LowerBound);
|
||||
} else {
|
||||
MP_VERBOSE(af, "... , ");
|
||||
}
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
|
||||
MP_VERBOSE(af, "%0.2f]\n", hint.UpperBound);
|
||||
} else {
|
||||
MP_VERBOSE(af, "...]\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* This function might "slightly" look like dlopenLADSPA in the LADSPA SDK :-)
|
||||
* But, I changed a few things, because imho it was broken. It did not support
|
||||
* relative paths, only absolute paths that start with a /
|
||||
* I think ../../some/dir/foobar.so is just as valid. And if one wants to call
|
||||
* his library '...somename...so' he's crazy, but it should be allowed.
|
||||
* So, search the path first, try plain *filename later.
|
||||
* Also, try adding .so first! I like the recursion the SDK did, but it's
|
||||
* better the other way around. -af ladspa=cmt:amp_stereo:0.5 is easier to type
|
||||
* than -af ladspa=cmt.so:amp_stereo:0.5 :-))
|
||||
*/
|
||||
|
||||
/** \brief dlopen() wrapper
|
||||
*
|
||||
* This is a wrapper around dlopen(). It tries various variations of the
|
||||
* filename (with or without the addition of the .so extension) in various
|
||||
* directories specified by the LADSPA_PATH environment variable. If all fails
|
||||
* it tries the filename directly as an absolute path to the library.
|
||||
*
|
||||
* \param filename filename of the library to load.
|
||||
* \param flag see dlopen(3) for a description of the flags.
|
||||
*
|
||||
* \return returns a pointer to the loaded library on success, or
|
||||
* NULL if it fails to load.
|
||||
*/
|
||||
|
||||
static void* mydlopen(const char *filename, int flag) {
|
||||
char *buf;
|
||||
const char *end, *start, *ladspapath;
|
||||
int endsinso;
|
||||
size_t filenamelen;
|
||||
void *result = NULL;
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
/* For Windows there's only absolute path support.
|
||||
* If you have a Windows machine, feel free to fix this.
|
||||
* (path separator, shared objects extension, et cetera). */
|
||||
MP_VERBOSE(af, "\ton windows, only absolute pathnames "
|
||||
"are supported\n");
|
||||
MP_VERBOSE(af, "\ttrying %s\n", filename);
|
||||
return dlopen(filename, flag);
|
||||
#endif
|
||||
|
||||
filenamelen = strlen(filename);
|
||||
|
||||
endsinso = 0;
|
||||
if (filenamelen > 3)
|
||||
endsinso = (strcmp(filename+filenamelen-3, ".so") == 0);
|
||||
if (!endsinso) {
|
||||
buf=malloc(filenamelen+4);
|
||||
strcpy(buf, filename);
|
||||
strcat(buf, ".so");
|
||||
result=mydlopen(buf, flag);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
ladspapath=getenv("LADSPA_PATH");
|
||||
|
||||
if (ladspapath) {
|
||||
start=ladspapath;
|
||||
while (*start != '\0') {
|
||||
int needslash;
|
||||
end=start;
|
||||
while ( (*end != ':') && (*end != '\0') )
|
||||
end++;
|
||||
|
||||
buf=malloc(filenamelen + 2 + (end-start) );
|
||||
if (end > start)
|
||||
strncpy(buf, start, end-start);
|
||||
needslash=0;
|
||||
if (end > start)
|
||||
if (*(end-1) != '/') {
|
||||
needslash = 1;
|
||||
buf[end-start] = '/';
|
||||
}
|
||||
strcpy(buf+needslash+(end-start), filename);
|
||||
|
||||
result=dlopen(buf, flag);
|
||||
|
||||
free(buf);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
start = end;
|
||||
if (*start == ':')
|
||||
start++;
|
||||
} /* end while there's still more in the path */
|
||||
} /* end if there's a ladspapath */
|
||||
|
||||
/* last resort, just open it again, so the dlerror() message is correct */
|
||||
return dlopen(filename,flag);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/** \brief Load a LADSPA Plugin
|
||||
*
|
||||
* This function loads the LADSPA plugin specified by the file and label
|
||||
* that are present in the setup variable. First, it loads the library.
|
||||
* If it fails, it returns AF_ERROR. If not, it continues to look for the
|
||||
* specified label. If it finds it, it sets the plugin_descriptor inside
|
||||
* setup and returns AF_OK. If it doesn't, it returns AF_ERROR. Special case
|
||||
* is a label called 'help'. In that case, it prints a list of all available
|
||||
* labels (filters) in the library specified by file.
|
||||
*
|
||||
* \param setup Current setup of the filter. Contains filename and label.
|
||||
*
|
||||
* \return Either AF_ERROR or AF_OK, depending on the success of the operation.
|
||||
*/
|
||||
|
||||
static int af_ladspa_load_plugin(struct af_instance *af) {
|
||||
af_ladspa_t *setup = af->priv;
|
||||
const LADSPA_Descriptor *ladspa_descriptor;
|
||||
LADSPA_Descriptor_Function descriptor_function;
|
||||
int i;
|
||||
|
||||
/* load library */
|
||||
MP_VERBOSE(af, "%s: loading ladspa plugin library %s\n",
|
||||
setup->myname, setup->file);
|
||||
|
||||
setup->libhandle = mydlopen(setup->file, RTLD_NOW);
|
||||
|
||||
if (!setup->libhandle) {
|
||||
MP_ERR(af, "%s: %s %s\n\t%s\n", setup->myname,
|
||||
_("failed to load"), setup->file, dlerror() );
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
MP_VERBOSE(af, "%s: library found.\n", setup->myname);
|
||||
|
||||
/* find descriptor function */
|
||||
dlerror();
|
||||
descriptor_function = (LADSPA_Descriptor_Function) dlsym (setup->libhandle,
|
||||
"ladspa_descriptor");
|
||||
|
||||
if (!descriptor_function) {
|
||||
MP_ERR(af, "%s: %s\n\t%s\n", setup->myname,
|
||||
_("Couldn't find ladspa_descriptor() function in the specified library file."), dlerror());
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
/* if label == help, list all labels in library and exit */
|
||||
|
||||
if (strcmp(setup->label, "help") == 0) {
|
||||
MP_INFO(af, "%s: %s %s:\n", setup->myname,
|
||||
_("available labels in"), setup->file);
|
||||
for (i=0; ; i++) {
|
||||
ladspa_descriptor = descriptor_function(i);
|
||||
if (ladspa_descriptor == NULL) {
|
||||
return AF_ERROR;
|
||||
}
|
||||
MP_INFO(af, " %-16s - %s (%lu)\n",
|
||||
ladspa_descriptor->Label,
|
||||
ladspa_descriptor->Name,
|
||||
ladspa_descriptor->UniqueID);
|
||||
}
|
||||
}
|
||||
|
||||
MP_VERBOSE(af, "%s: looking for label\n", setup->myname);
|
||||
|
||||
/* find label in library */
|
||||
for (i=0; ; i++) {
|
||||
ladspa_descriptor = descriptor_function(i);
|
||||
if (ladspa_descriptor == NULL) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("Couldn't find label in plugin library."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (strcmp(ladspa_descriptor->Label, setup->label) == 0) {
|
||||
setup->plugin_descriptor = ladspa_descriptor;
|
||||
MP_VERBOSE(af, "%s: %s found\n", setup->myname,
|
||||
setup->label);
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/** \brief Print a malloc() failed error message.
|
||||
*
|
||||
* Generic function which can be called if a call to malloc(), calloc(),
|
||||
* strdup(), et cetera, failed. It prints a message to the console and
|
||||
* returns AF_ERROR.
|
||||
*
|
||||
* \return AF_ERROR
|
||||
*/
|
||||
|
||||
static int af_ladspa_malloc_failed(char *myname) {
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/** \brief Controls the filter.
|
||||
*
|
||||
* Control the behaviour of the filter.
|
||||
*
|
||||
* Commands:
|
||||
* CONTROL_REINIT Sets the af structure with proper values for number
|
||||
* of channels, rate, format, et cetera.
|
||||
* \param af Audio filter instance
|
||||
* \param cmd The command to execute
|
||||
* \param arg Arguments to the command
|
||||
*
|
||||
* \return Either AF_ERROR or AF_OK, depending on the succes of the
|
||||
* operation.
|
||||
*/
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
|
||||
|
||||
switch(cmd) {
|
||||
case AF_CONTROL_REINIT:
|
||||
MP_VERBOSE(af, "%s: (re)init\n", setup->myname);
|
||||
|
||||
if (!arg) return AF_ERROR;
|
||||
|
||||
/* accept FLOAT, let af_format do conversion */
|
||||
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
|
||||
return af_test_output(af, (struct mp_audio*)arg);
|
||||
}
|
||||
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/** \brief Uninitialise the LADSPA Plugin Loader filter.
|
||||
*
|
||||
* This function deactivates the plugin(s), cleans up, frees all allocated
|
||||
* memory and exits.
|
||||
*
|
||||
* \return No return value.
|
||||
*/
|
||||
|
||||
static void uninit(struct af_instance *af) {
|
||||
if (af->priv) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
|
||||
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
|
||||
|
||||
if (setup->myname) {
|
||||
MP_VERBOSE(af, "%s: cleaning up\n", setup->myname);
|
||||
free(setup->myname);
|
||||
}
|
||||
|
||||
if (setup->chhandles) {
|
||||
for (int i = 0; i < setup->nch; i+=setup->ninputs) {
|
||||
if (pdes->deactivate) pdes->deactivate(setup->chhandles[i]);
|
||||
if (pdes->cleanup) pdes->cleanup(setup->chhandles[i]);
|
||||
}
|
||||
free(setup->chhandles);
|
||||
}
|
||||
|
||||
free(setup->inputcontrolsmap);
|
||||
free(setup->inputcontrols);
|
||||
free(setup->outputcontrolsmap);
|
||||
free(setup->outputcontrols);
|
||||
free(setup->inputs);
|
||||
free(setup->outputs);
|
||||
|
||||
if (setup->inbufs) {
|
||||
for(int i = 0; i < setup->nch; i++)
|
||||
free(setup->inbufs[i]);
|
||||
free(setup->inbufs);
|
||||
}
|
||||
|
||||
if (setup->outbufs) {
|
||||
for (int i = 0; i < setup->nch; i++)
|
||||
free(setup->outbufs[i]);
|
||||
free(setup->outbufs);
|
||||
}
|
||||
|
||||
if (setup->libhandle)
|
||||
dlclose(setup->libhandle);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/** \brief Process chunk of audio data through the selected LADSPA Plugin.
|
||||
*
|
||||
* \param af Pointer to audio filter instance
|
||||
* \param data Pointer to chunk of audio data
|
||||
*
|
||||
* \return Either AF_ERROR or AF_OK
|
||||
*/
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
af_ladspa_t *setup = af->priv;
|
||||
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
|
||||
float *audio = (float*)data->planes[0];
|
||||
int nsamples = data->samples*data->nch;
|
||||
int nch = data->nch;
|
||||
int rate = data->rate;
|
||||
int i, p;
|
||||
|
||||
if (setup->status !=AF_OK) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
if (af_make_writeable(af, data) < 0) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* See if it's the first call. If so, setup inbufs/outbufs, instantiate
|
||||
* plugin, connect ports and activate plugin
|
||||
*/
|
||||
|
||||
if ( (setup->bufsize != nsamples/nch) || (setup->nch != nch) ) {
|
||||
|
||||
/* if setup->nch==0, it's the first call, if not, something has
|
||||
* changed and all previous mallocs have to be freed
|
||||
*/
|
||||
|
||||
if (setup->nch != 0) {
|
||||
MP_TRACE(af, "%s: bufsize change; free old buffer\n",
|
||||
setup->myname);
|
||||
|
||||
if(setup->inbufs) {
|
||||
for(i=0; i<setup->nch; i++)
|
||||
free(setup->inbufs[i]);
|
||||
free(setup->inbufs);
|
||||
}
|
||||
if(setup->outbufs) {
|
||||
for(i=0; i<setup->nch; i++)
|
||||
free(setup->outbufs[i]);
|
||||
free(setup->outbufs);
|
||||
}
|
||||
} /* everything is freed */
|
||||
|
||||
setup->bufsize = nsamples/nch;
|
||||
setup->nch = nch;
|
||||
|
||||
setup->inbufs = calloc(nch, sizeof(float*));
|
||||
setup->outbufs = calloc(nch, sizeof(float*));
|
||||
|
||||
MP_TRACE(af, "%s: bufsize = %d\n",
|
||||
setup->myname, setup->bufsize);
|
||||
|
||||
for(i=0; i<nch; i++) {
|
||||
setup->inbufs[i] = calloc(setup->bufsize, sizeof(float));
|
||||
setup->outbufs[i] = calloc(setup->bufsize, sizeof(float));
|
||||
}
|
||||
|
||||
/* only on the first call, there are no handles. */
|
||||
|
||||
if (!setup->chhandles) {
|
||||
setup->chhandles = calloc(nch, sizeof(LADSPA_Handle));
|
||||
|
||||
/* create handles
|
||||
* for stereo effects, create one handle for two channels
|
||||
*/
|
||||
|
||||
for(i=0; i<nch; i++) {
|
||||
|
||||
if (i % setup->ninputs) { /* stereo effect */
|
||||
/* copy the handle from previous channel */
|
||||
setup->chhandles[i] = setup->chhandles[i-1];
|
||||
continue;
|
||||
}
|
||||
|
||||
setup->chhandles[i] = pdes->instantiate(pdes, rate);
|
||||
}
|
||||
}
|
||||
|
||||
/* connect input/output ports for each channel/filter instance
|
||||
*
|
||||
* always (re)connect ports
|
||||
*/
|
||||
|
||||
for(i=0; i<nch; i++) {
|
||||
pdes->connect_port(setup->chhandles[i],
|
||||
setup->inputs[i % setup->ninputs],
|
||||
setup->inbufs[i]);
|
||||
pdes->connect_port(setup->chhandles[i],
|
||||
setup->outputs[i % setup->ninputs],
|
||||
setup->outbufs[i]);
|
||||
|
||||
/* connect (input) controls */
|
||||
|
||||
for (p=0; p<setup->nports; p++) {
|
||||
LADSPA_PortDescriptor d = pdes->PortDescriptors[p];
|
||||
if (LADSPA_IS_PORT_CONTROL(d)) {
|
||||
if (LADSPA_IS_PORT_INPUT(d)) {
|
||||
pdes->connect_port(setup->chhandles[i], p,
|
||||
&(setup->inputcontrols[p]) );
|
||||
} else {
|
||||
pdes->connect_port(setup->chhandles[i], p,
|
||||
&(setup->outputcontrols[p]) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Activate filter (if it isn't already :) ) */
|
||||
|
||||
if (pdes->activate && !setup->activated && i % setup->ninputs == 0)
|
||||
pdes->activate(setup->chhandles[i]);
|
||||
|
||||
} /* All channels/filters done! except for... */
|
||||
setup->activated = 1;
|
||||
|
||||
/* Stereo effect with one channel left. Use same buffer for left
|
||||
* and right. connect it to the second port.
|
||||
*/
|
||||
|
||||
for (p = i; p % setup->ninputs; p++) {
|
||||
pdes->connect_port(setup->chhandles[i-1],
|
||||
setup->inputs[p % setup->ninputs],
|
||||
setup->inbufs[i-1]);
|
||||
pdes->connect_port(setup->chhandles[i-1],
|
||||
setup->outputs[p % setup->ninputs],
|
||||
setup->outbufs[i-1]);
|
||||
} /* done! */
|
||||
|
||||
} /* setup for first call/change of bufsize is done.
|
||||
* normal playing routine follows...
|
||||
*/
|
||||
|
||||
/* Right now, I use a separate input and output buffer.
|
||||
* I could change this to in-place processing (inbuf==outbuf), but some
|
||||
* ladspa filters are broken and are not able to handle that. This seems
|
||||
* fast enough, so unless somebody complains, it stays this way :)
|
||||
*/
|
||||
|
||||
/* Fill inbufs */
|
||||
|
||||
for (p=0; p<setup->bufsize; p++) {
|
||||
for (i=0; i<nch; i++) {
|
||||
setup->inbufs[i][p] = audio[p*nch + i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Run filter(s) */
|
||||
|
||||
for (i=0; i<nch; i+=setup->ninputs) {
|
||||
pdes->run(setup->chhandles[i], setup->bufsize);
|
||||
}
|
||||
|
||||
/* Extract outbufs */
|
||||
|
||||
for (p=0; p<setup->bufsize; p++) {
|
||||
for (i=0; i<nch; i++) {
|
||||
audio[p*nch + i] = setup->outbufs[i][p];
|
||||
}
|
||||
}
|
||||
|
||||
/* done */
|
||||
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/** \brief Open LADSPA Plugin Loader Filter
|
||||
*
|
||||
* \param af Audio Filter instance
|
||||
*
|
||||
* \return Either AF_ERROR or AF_OK
|
||||
*/
|
||||
|
||||
static int af_open(struct af_instance *af) {
|
||||
|
||||
af->control=control;
|
||||
af->uninit=uninit;
|
||||
af->filter_frame = filter_frame;
|
||||
|
||||
af_ladspa_t *setup = af->priv;
|
||||
|
||||
setup->status = AF_ERROR; /* will be set to AF_OK if
|
||||
* all went OK and filter()
|
||||
* should proceed.
|
||||
*/
|
||||
|
||||
setup->myname = strdup(af_info_ladspa.name);
|
||||
if (!setup->myname)
|
||||
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
|
||||
|
||||
if (!setup->file || !setup->file[0]) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("No library file specified."));
|
||||
uninit(af);
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (!setup->label || !setup->label[0]) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("No filter label specified."));
|
||||
uninit(af);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
free(setup->myname);
|
||||
setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
|
||||
strlen(setup->label)+6, 1);
|
||||
snprintf(setup->myname, strlen(af_info_ladspa.name)+
|
||||
strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
|
||||
af_info_ladspa.name, setup->file, setup->label);
|
||||
|
||||
/* load plugin :) */
|
||||
|
||||
if ( af_ladspa_load_plugin(af) != AF_OK )
|
||||
return AF_ERROR;
|
||||
|
||||
/* see what inputs, outputs and controls this plugin has */
|
||||
if ( af_ladspa_parse_plugin(af) != AF_OK )
|
||||
return AF_ERROR;
|
||||
|
||||
/* ninputcontrols is set by now, read control values from arg */
|
||||
|
||||
float val;
|
||||
char *line = setup->controls;
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
if (!line || (i != 0 && *line != ',')) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("Not enough controls specified on the command line."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (i != 0)
|
||||
line++;
|
||||
if (sscanf(line, "%f", &val) != 1) {
|
||||
MP_ERR(af, "%s: %s\n", setup->myname,
|
||||
_("Not enough controls specified on the command line."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
|
||||
line = strchr(line, ',');
|
||||
}
|
||||
|
||||
MP_VERBOSE(af, "%s: input controls: ", setup->myname);
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
MP_VERBOSE(af, "%0.4f ",
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]]);
|
||||
}
|
||||
MP_VERBOSE(af, "\n");
|
||||
|
||||
/* check boundaries of inputcontrols */
|
||||
|
||||
MP_VERBOSE(af, "%s: checking boundaries of input controls\n",
|
||||
setup->myname);
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
int p = setup->inputcontrolsmap[i];
|
||||
LADSPA_PortRangeHint hint =
|
||||
setup->plugin_descriptor->PortRangeHints[p];
|
||||
val = setup->inputcontrols[p];
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
|
||||
val < hint.LowerBound) {
|
||||
MP_ERR(af, "%s: Input control #%d is below lower boundary of %0.4f.\n",
|
||||
setup->myname, i, hint.LowerBound);
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
|
||||
val > hint.UpperBound) {
|
||||
MP_ERR(af, "%s: Input control #%d is above upper boundary of %0.4f.\n",
|
||||
setup->myname, i, hint.UpperBound);
|
||||
return AF_ERROR;
|
||||
}
|
||||
}
|
||||
MP_VERBOSE(af, "%s: all controls have sane values\n",
|
||||
setup->myname);
|
||||
|
||||
/* All is well! */
|
||||
setup->status = AF_OK;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@@ -146,7 +146,7 @@ static void uninit(struct af_instance* af)
|
||||
af_ac3enc_t *s = af->priv;
|
||||
|
||||
if (s) {
|
||||
av_free_packet(&s->pkt);
|
||||
av_packet_unref(&s->pkt);
|
||||
if(s->lavc_actx) {
|
||||
avcodec_close(s->lavc_actx);
|
||||
av_free(s->lavc_actx);
|
||||
|
||||
+28
-3
@@ -34,11 +34,14 @@
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
#include "af.h"
|
||||
|
||||
#include "common/av_common.h"
|
||||
#include "common/tags.h"
|
||||
|
||||
#include "options/m_option.h"
|
||||
|
||||
@@ -63,6 +66,8 @@ struct priv {
|
||||
|
||||
bool eof;
|
||||
|
||||
struct mp_tags *metadata;
|
||||
|
||||
// options
|
||||
char *cfg_graph;
|
||||
char **cfg_avopts;
|
||||
@@ -190,6 +195,10 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
if (af_to_avformat(in->format) == AV_SAMPLE_FMT_NONE)
|
||||
mp_audio_set_format(in, AF_FORMAT_FLOAT);
|
||||
|
||||
// Removing this requires fixing AVFrame.data vs. AVFrame.extended_data
|
||||
if (in->channels.num > AV_NUM_DATA_POINTERS)
|
||||
return AF_ERROR;
|
||||
|
||||
if (!mp_chmap_is_lavc(&in->channels))
|
||||
mp_chmap_reorder_to_lavc(&in->channels); // will always work
|
||||
|
||||
@@ -206,13 +215,19 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
mp_chmap_from_lavc(&out_cm, l_out->channel_layout);
|
||||
mp_audio_set_channels(out, &out_cm);
|
||||
|
||||
if (!mp_audio_config_valid(out))
|
||||
if (!mp_audio_config_valid(out) || out->channels.num > AV_NUM_DATA_POINTERS)
|
||||
return AF_ERROR;
|
||||
|
||||
p->timebase_out = l_out->time_base;
|
||||
|
||||
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
|
||||
}
|
||||
case AF_CONTROL_GET_METADATA:
|
||||
if (p->metadata) {
|
||||
*(struct mp_tags *)arg = *p->metadata;
|
||||
return CONTROL_OK;
|
||||
}
|
||||
return CONTROL_NA;
|
||||
case AF_CONTROL_RESET:
|
||||
reset(af);
|
||||
return AF_OK;
|
||||
@@ -220,6 +235,17 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static void get_metadata_from_av_frame(struct af_instance *af, AVFrame *frame)
|
||||
{
|
||||
#if HAVE_AVFRAME_METADATA
|
||||
struct priv *p = af->priv;
|
||||
if (!p->metadata)
|
||||
p->metadata = talloc_zero(p, struct mp_tags);
|
||||
|
||||
mp_tags_copy_from_av_dictionary(p->metadata, av_frame_get_metadata(frame));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
struct priv *p = af->priv;
|
||||
@@ -307,6 +333,7 @@ static int filter_out(struct af_instance *af)
|
||||
af->delay = in_time - out_time;
|
||||
}
|
||||
|
||||
get_metadata_from_av_frame(af, frame);
|
||||
af_add_output_frame(af, out);
|
||||
av_frame_free(&frame);
|
||||
return 0;
|
||||
@@ -326,8 +353,6 @@ static int af_open(struct af_instance *af)
|
||||
af->uninit = uninit;
|
||||
af->filter_frame = filter_frame;
|
||||
af->filter_out = filter_out;
|
||||
// Removing this requires fixing AVFrame.data vs. AVFrame.extended_data
|
||||
assert(MP_NUM_CHANNELS <= AV_NUM_DATA_POINTERS);
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/samplefmt.h>
|
||||
#include <libavutil/channel_layout.h>
|
||||
#include <libavutil/mathematics.h>
|
||||
|
||||
#include "common/common.h"
|
||||
@@ -48,6 +49,7 @@
|
||||
#define avresample_convert(ctx, out, out_planesize, out_samples, in, in_planesize, in_samples) \
|
||||
swr_convert(ctx, out, out_samples, (const uint8_t**)(in), in_samples)
|
||||
#define avresample_set_channel_mapping swr_set_channel_mapping
|
||||
#define avresample_set_compensation swr_set_compensation
|
||||
#else
|
||||
#error "config.h broken or no resampler found"
|
||||
#endif
|
||||
@@ -64,14 +66,7 @@ struct af_resample_opts {
|
||||
int phase_shift;
|
||||
int linear;
|
||||
double cutoff;
|
||||
|
||||
int in_rate_af; // filter input sample rate
|
||||
int in_rate; // actual rate (used by lavr), adjusted for playback speed
|
||||
int in_format;
|
||||
struct mp_chmap in_channels;
|
||||
int out_rate;
|
||||
int out_format;
|
||||
struct mp_chmap out_channels;
|
||||
int normalize;
|
||||
};
|
||||
|
||||
struct af_resample {
|
||||
@@ -83,19 +78,28 @@ struct af_resample {
|
||||
struct mp_audio pool_fmt; // format used to allocate frames for avrctx output
|
||||
struct mp_audio pre_out_fmt; // format before final conversion (S24)
|
||||
struct AVAudioResampleContext *avrctx_out; // for output channel reordering
|
||||
struct af_resample_opts ctx; // opts in the context
|
||||
struct af_resample_opts opts; // opts requested by the user
|
||||
// At least libswresample keeps a pointer around for this:
|
||||
int reorder_in[MP_NUM_CHANNELS];
|
||||
int reorder_out[MP_NUM_CHANNELS];
|
||||
struct mp_audio_pool *reorder_buffer;
|
||||
|
||||
int in_rate_af; // filter input sample rate
|
||||
int in_rate; // actual rate (used by lavr), adjusted for playback speed
|
||||
int in_format;
|
||||
struct mp_chmap in_channels;
|
||||
int out_rate;
|
||||
int out_format;
|
||||
struct mp_chmap out_channels;
|
||||
|
||||
double missing_samples; // fractional samples not yet output
|
||||
};
|
||||
|
||||
#if HAVE_LIBAVRESAMPLE
|
||||
static double get_delay(struct af_resample *s)
|
||||
{
|
||||
return avresample_get_delay(s->avrctx) / (double)s->ctx.in_rate +
|
||||
avresample_available(s->avrctx) / (double)s->ctx.out_rate;
|
||||
return avresample_get_delay(s->avrctx) / (double)s->in_rate +
|
||||
avresample_available(s->avrctx) / (double)s->out_rate;
|
||||
}
|
||||
static void drop_all_output(struct af_resample *s)
|
||||
{
|
||||
@@ -108,7 +112,7 @@ static int get_out_samples(struct af_resample *s, int in_samples)
|
||||
#else
|
||||
static double get_delay(struct af_resample *s)
|
||||
{
|
||||
int64_t base = s->ctx.in_rate * (int64_t)s->ctx.out_rate;
|
||||
int64_t base = s->in_rate * (int64_t)s->out_rate;
|
||||
return swr_get_delay(s->avrctx, base) / (double)base;
|
||||
}
|
||||
static void drop_all_output(struct af_resample *s)
|
||||
@@ -120,8 +124,8 @@ static int get_out_samples(struct af_resample *s, int in_samples)
|
||||
#if LIBSWRESAMPLE_VERSION_MAJOR > 1 || LIBSWRESAMPLE_VERSION_MINOR >= 2
|
||||
return swr_get_out_samples(s->avrctx, in_samples);
|
||||
#else
|
||||
return av_rescale_rnd(in_samples, s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP)
|
||||
+ swr_get_delay(s->avrctx, s->ctx.out_rate);
|
||||
return av_rescale_rnd(in_samples, s->out_rate, s->in_rate, AV_ROUND_UP)
|
||||
+ swr_get_delay(s->avrctx, s->out_rate);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -214,26 +218,26 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
out_samplefmtp == AV_SAMPLE_FMT_NONE)
|
||||
goto error;
|
||||
|
||||
s->ctx.out_rate = out->rate;
|
||||
s->ctx.in_rate_af = in->rate;
|
||||
s->ctx.in_rate = rate_from_speed(in->rate, s->playback_speed);
|
||||
s->ctx.out_format = out->format;
|
||||
s->ctx.in_format = in->format;
|
||||
s->ctx.out_channels= out->channels;
|
||||
s->ctx.in_channels = in->channels;
|
||||
s->ctx.filter_size = s->opts.filter_size;
|
||||
s->ctx.phase_shift = s->opts.phase_shift;
|
||||
s->ctx.linear = s->opts.linear;
|
||||
s->ctx.cutoff = s->opts.cutoff;
|
||||
s->out_rate = out->rate;
|
||||
s->in_rate_af = in->rate;
|
||||
s->in_rate = rate_from_speed(in->rate, s->playback_speed);
|
||||
s->out_format = out->format;
|
||||
s->in_format = in->format;
|
||||
s->out_channels= out->channels;
|
||||
s->in_channels = in->channels;
|
||||
|
||||
av_opt_set_int(s->avrctx, "filter_size", s->ctx.filter_size, 0);
|
||||
av_opt_set_int(s->avrctx, "phase_shift", s->ctx.phase_shift, 0);
|
||||
av_opt_set_int(s->avrctx, "linear_interp", s->ctx.linear, 0);
|
||||
s->missing_samples = 0;
|
||||
|
||||
av_opt_set_double(s->avrctx, "cutoff", s->ctx.cutoff, 0);
|
||||
av_opt_set_int(s->avrctx, "filter_size", s->opts.filter_size, 0);
|
||||
av_opt_set_int(s->avrctx, "phase_shift", s->opts.phase_shift, 0);
|
||||
av_opt_set_int(s->avrctx, "linear_interp", s->opts.linear, 0);
|
||||
|
||||
av_opt_set_double(s->avrctx, "cutoff", s->opts.cutoff, 0);
|
||||
|
||||
#if HAVE_LIBSWRESAMPLE
|
||||
av_opt_set_double(s->avrctx, "rematrix_maxval", 1.0, 0);
|
||||
av_opt_set_double(s->avrctx, "rematrix_maxval", s->opts.normalize ? 1 : 1000, 0);
|
||||
#else
|
||||
av_opt_set_int(s->avrctx, "normalize_mix_level", s->opts.normalize, 0);
|
||||
#endif
|
||||
|
||||
if (mp_set_avopts(af->log, s->avrctx, s->avopts) < 0)
|
||||
@@ -300,23 +304,24 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
// Real conversion; output is input to avrctx_out.
|
||||
av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx, "in_sample_rate", s->ctx.in_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_rate", s->ctx.out_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "in_sample_rate", s->in_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_rate", s->out_rate, 0);
|
||||
av_opt_set_int(s->avrctx, "in_sample_fmt", in_samplefmt, 0);
|
||||
av_opt_set_int(s->avrctx, "out_sample_fmt", out_samplefmtp, 0);
|
||||
|
||||
// Just needs the correct number of channels.
|
||||
int fake_out_ch_layout = av_get_default_channel_layout(map_out.num);
|
||||
// Just needs the correct number of channels for deplanarization.
|
||||
struct mp_chmap fake_chmap;
|
||||
mp_chmap_set_unknown(&fake_chmap, map_out.num);
|
||||
uint64_t fake_out_ch_layout = mp_chmap_to_lavc_unchecked(&fake_chmap);
|
||||
if (!fake_out_ch_layout)
|
||||
goto error;
|
||||
|
||||
// Deplanarize if needed.
|
||||
av_opt_set_int(s->avrctx_out, "in_channel_layout", fake_out_ch_layout, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_channel_layout", fake_out_ch_layout, 0);
|
||||
|
||||
av_opt_set_int(s->avrctx_out, "in_sample_fmt", out_samplefmtp, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_sample_fmt", out_samplefmt, 0);
|
||||
av_opt_set_int(s->avrctx_out, "in_sample_rate", s->ctx.out_rate, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_sample_rate", s->ctx.out_rate, 0);
|
||||
av_opt_set_int(s->avrctx_out, "in_sample_rate", s->out_rate, 0);
|
||||
av_opt_set_int(s->avrctx_out, "out_sample_rate", s->out_rate, 0);
|
||||
|
||||
// API has weird requirements, quoting avresample.h:
|
||||
// * This function can only be called when the allocated context is not open.
|
||||
@@ -387,14 +392,6 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
return AF_OK;
|
||||
case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
|
||||
s->playback_speed = *(double *)arg;
|
||||
int new_rate = rate_from_speed(s->ctx.in_rate_af, s->playback_speed);
|
||||
if (new_rate != s->ctx.in_rate && s->avrctx && af->fmt_out.format) {
|
||||
// Before reconfiguring, drain the audio that is still buffered
|
||||
// in the resampler.
|
||||
af->filter_frame(af, NULL);
|
||||
// Reinitialize resampler.
|
||||
configure_lavrr(af, &af->fmt_in, &af->fmt_out, false);
|
||||
}
|
||||
return AF_OK;
|
||||
}
|
||||
case AF_CONTROL_RESET:
|
||||
@@ -456,7 +453,7 @@ static void reorder_planes(struct mp_audio *mpa, int *reorder,
|
||||
}
|
||||
}
|
||||
|
||||
static int filter(struct af_instance *af, struct mp_audio *in)
|
||||
static int filter_resample(struct af_instance *af, struct mp_audio *in)
|
||||
{
|
||||
struct af_resample *s = af->priv;
|
||||
|
||||
@@ -517,6 +514,36 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int filter(struct af_instance *af, struct mp_audio *in)
|
||||
{
|
||||
struct af_resample *s = af->priv;
|
||||
|
||||
int new_rate = rate_from_speed(s->in_rate_af, s->playback_speed);
|
||||
bool need_reinit = fabs(new_rate / (double)s->in_rate - 1) > 0.01;
|
||||
|
||||
if (!need_reinit && s->avrctx) {
|
||||
double speed_factor = s->playback_speed * s->in_rate_af / s->in_rate;
|
||||
int in_samples = in ? in->samples : 0;
|
||||
double wanted_samples = in_samples / speed_factor + s->missing_samples;
|
||||
int wanted_samples_i = lrint(wanted_samples);
|
||||
s->missing_samples = wanted_samples - wanted_samples_i;
|
||||
if (avresample_set_compensation(s->avrctx,
|
||||
(wanted_samples_i - in_samples) * s->out_rate / s->in_rate,
|
||||
wanted_samples_i * s->out_rate / s->in_rate) < 0)
|
||||
need_reinit = true;
|
||||
}
|
||||
|
||||
if (need_reinit && new_rate != s->in_rate) {
|
||||
// Before reconfiguring, drain the audio that is still buffered
|
||||
// in the resampler.
|
||||
filter_resample(af, NULL);
|
||||
// Reinitialize resampler.
|
||||
configure_lavrr(af, &af->fmt_in, &af->fmt_out, false);
|
||||
}
|
||||
|
||||
return filter_resample(af, in);
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance *af)
|
||||
{
|
||||
struct af_resample *s = af->priv;
|
||||
@@ -545,6 +572,7 @@ const struct af_info af_info_lavrresample = {
|
||||
.filter_size = 16,
|
||||
.cutoff = 0.0,
|
||||
.phase_shift = 10,
|
||||
.normalize = 1,
|
||||
},
|
||||
.playback_speed = 1.0,
|
||||
.allow_detach = 1,
|
||||
@@ -555,6 +583,7 @@ const struct af_info af_info_lavrresample = {
|
||||
OPT_FLAG("linear", opts.linear, 0),
|
||||
OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1),
|
||||
OPT_FLAG("detach", allow_detach, 0),
|
||||
OPT_FLAG("normalize", opts.normalize, 0),
|
||||
OPT_KEYVALUELIST("o", avopts, 0),
|
||||
{0}
|
||||
},
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Michael Niedermayer
|
||||
* Copyright (C) 2004 Alex Beregszaszi
|
||||
* based upon af_extrastereo.c by Pierre Lombard
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "af.h"
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_sinesuppress_s
|
||||
{
|
||||
double freq;
|
||||
double decay;
|
||||
double real;
|
||||
double imag;
|
||||
double ref;
|
||||
double pos;
|
||||
}af_sinesuppress_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
mp_audio_copy_config(af->data, (struct mp_audio*)arg);
|
||||
mp_audio_set_num_channels(af->data, 1);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16);
|
||||
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int play_s16(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
if (af_make_writeable(af, data) < 0) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
af_sinesuppress_t *s = af->priv;
|
||||
register int i = 0;
|
||||
int16_t *a = (int16_t*)data->planes[0]; // Audio data
|
||||
int len = data->samples*data->nch; // Number of samples
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
double co= cos(s->pos);
|
||||
double si= sin(s->pos);
|
||||
|
||||
s->real += co * a[i];
|
||||
s->imag += si * a[i];
|
||||
s->ref += co * co;
|
||||
|
||||
a[i] -= (s->real * co + s->imag * si) / s->ref;
|
||||
|
||||
s->real -= s->real * s->decay;
|
||||
s->imag -= s->imag * s->decay;
|
||||
s->ref -= s->ref * s->decay;
|
||||
|
||||
s->pos += 2 * M_PI * s->freq / data->rate;
|
||||
}
|
||||
|
||||
MP_VERBOSE(af, "f:%8.2f: amp:%8.2f\n", s->freq, sqrt(s->real*s->real + s->imag*s->imag) / s->ref);
|
||||
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = play_s16;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_sinesuppress_t
|
||||
const struct af_info af_info_sinesuppress = {
|
||||
.info = "Sine Suppress",
|
||||
.name = "sinesuppress",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_sinesuppress_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_DOUBLE("freq", freq, 0, OPTDEF_DOUBLE(50.0)),
|
||||
OPT_DOUBLE("decay", decay, 0, OPTDEF_DOUBLE(0.0001)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This filter adds a sub-woofer channels to the audio stream by
|
||||
averaging the left and right channel and low-pass filter them. The
|
||||
low-pass filter is implemented as a 4th order IIR Butterworth
|
||||
filter, with a variable cutoff frequency between 10 and 300 Hz. The
|
||||
filter gives 24dB/octave attenuation. There are two runtime
|
||||
controls one for setting which channel to insert the sub-audio into
|
||||
called AF_CONTROL_SUB_CH and one for setting the cutoff frequency
|
||||
called AF_CONTROL_SUB_FC.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "af.h"
|
||||
#include "dsp.h"
|
||||
|
||||
// Q value for low-pass filter
|
||||
#define Q 1.0
|
||||
|
||||
// Analog domain biquad section
|
||||
typedef struct{
|
||||
float a[3]; // Numerator coefficients
|
||||
float b[3]; // Denominator coefficients
|
||||
} biquad_t;
|
||||
|
||||
// S-parameters for designing 4th order Butterworth filter
|
||||
static const biquad_t sp[2] = {{{1.0,0.0,0.0},{1.0,0.765367,1.0}},
|
||||
{{1.0,0.0,0.0},{1.0,1.847759,1.0}}};
|
||||
|
||||
// Data for specific instances of this filter
|
||||
typedef struct af_sub_s
|
||||
{
|
||||
float w[2][4]; // Filter taps for low-pass filter
|
||||
float q[2][2]; // Circular queues
|
||||
float fc; // Cutoff frequency [Hz] for low-pass filter
|
||||
float k; // Filter gain;
|
||||
int ch; // Channel number which to insert the filtered data
|
||||
|
||||
}af_sub_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_sub_t* s = af->priv;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
// Sanity check
|
||||
if(!arg) return AF_ERROR;
|
||||
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
mp_audio_set_channels_old(af->data, MPMAX(s->ch+1,((struct mp_audio*)arg)->nch));
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
|
||||
// Design low-pass filter
|
||||
s->k = 1.0;
|
||||
if((-1 == af_filter_szxform(sp[0].a, sp[0].b, Q, s->fc,
|
||||
(float)af->data->rate, &s->k, s->w[0])) ||
|
||||
(-1 == af_filter_szxform(sp[1].a, sp[1].b, Q, s->fc,
|
||||
(float)af->data->rate, &s->k, s->w[1])))
|
||||
return AF_ERROR;
|
||||
return af_test_output(af,(struct mp_audio*)arg);
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
#ifndef IIR
|
||||
#define IIR(in,w,q,out) { \
|
||||
float h0 = (q)[0]; \
|
||||
float h1 = (q)[1]; \
|
||||
float hn = (in) - h0 * (w)[0] - h1 * (w)[1]; \
|
||||
out = hn + h0 * (w)[2] + h1 * (w)[3]; \
|
||||
(q)[1] = h0; \
|
||||
(q)[0] = hn; \
|
||||
}
|
||||
#endif
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
if (af_make_writeable(af, data) < 0) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mp_audio* c = data; // Current working data
|
||||
af_sub_t* s = af->priv; // Setup for this instance
|
||||
float* a = c->planes[0]; // Audio data
|
||||
int len = c->samples*c->nch; // Number of samples in current audio block
|
||||
int nch = c->nch; // Number of channels
|
||||
int ch = s->ch; // Channel in which to insert the sub audio
|
||||
register int i;
|
||||
|
||||
// Run filter
|
||||
for(i=0;i<len;i+=nch){
|
||||
// Average left and right
|
||||
register float x = 0.5 * (a[i] + a[i+1]);
|
||||
IIR(x * s->k, s->w[0], s->q[0], x);
|
||||
IIR(x , s->w[1], s->q[1], a[i+ch]);
|
||||
}
|
||||
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory and set function pointers
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = filter_frame;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_sub_t
|
||||
const struct af_info af_info_sub = {
|
||||
.info = "Audio filter for adding a sub-base channel",
|
||||
.name = "sub",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_sub_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLOATRANGE("fc", fc, 0, 20, 300, OPTDEF_FLOAT(60.0)),
|
||||
OPT_INTRANGE("ch", ch, 0, 0, AF_NCH - 1, OPTDEF_INT(5)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Filter to do simple decoding of matrixed surround sound.
|
||||
* This will provide a (basic) surround-sound effect from
|
||||
* audio encoded for Dolby Surround, Pro Logic etc.
|
||||
*
|
||||
* Original author: Steve Davies <steve@daviesfam.org>
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* The principle: Make rear channels by extracting anti-phase data
|
||||
from the front channels, delay by 20ms and feed to rear in anti-phase
|
||||
*/
|
||||
|
||||
|
||||
/* SPLITREAR: Define to decode two distinct rear channels - this
|
||||
doesn't work so well in practice because separation in a passive
|
||||
matrix is not high. C (dialogue) to Ls and Rs 14dB or so - so
|
||||
dialogue leaks to the rear. Still - give it a try and send
|
||||
feedback. Comment this define for old behavior of a single
|
||||
surround sent to rear in anti-phase */
|
||||
#define SPLITREAR 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "af.h"
|
||||
#include "dsp.h"
|
||||
|
||||
#define L 32 // Length of fir filter
|
||||
#define LD 65536 // Length of delay buffer
|
||||
|
||||
// 32 Tap fir filter loop unrolled
|
||||
#define FIR(x,w,y) \
|
||||
y = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \
|
||||
+ w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \
|
||||
+ w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \
|
||||
+ w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] \
|
||||
+ w[16]*x[16]+w[17]*x[17]+w[18]*x[18]+w[19]*x[19] \
|
||||
+ w[20]*x[20]+w[21]*x[21]+w[22]*x[22]+w[23]*x[23] \
|
||||
+ w[24]*x[24]+w[25]*x[25]+w[26]*x[26]+w[27]*x[27] \
|
||||
+ w[28]*x[28]+w[29]*x[29]+w[30]*x[30]+w[31]*x[31])
|
||||
|
||||
// Add to circular queue macro + update index
|
||||
#ifdef SPLITREAR
|
||||
#define ADDQUE(qi,rq,lq,r,l)\
|
||||
lq[qi]=lq[qi+L]=(l);\
|
||||
rq[qi]=rq[qi+L]=(r);\
|
||||
qi=(qi-1)&(L-1);
|
||||
#else
|
||||
#define ADDQUE(qi,lq,l)\
|
||||
lq[qi]=lq[qi+L]=(l);\
|
||||
qi=(qi-1)&(L-1);
|
||||
#endif
|
||||
|
||||
// Macro for updating queue index in delay queues
|
||||
#define UPDATEQI(qi) qi=(qi+1)&(LD-1)
|
||||
|
||||
// instance data
|
||||
typedef struct af_surround_s
|
||||
{
|
||||
float lq[2*L]; // Circular queue for filtering left rear channel
|
||||
float rq[2*L]; // Circular queue for filtering right rear channel
|
||||
float w[L]; // FIR filter coefficients for surround sound 7kHz low-pass
|
||||
float* dr; // Delay queue right rear channel
|
||||
float* dl; // Delay queue left rear channel
|
||||
float d; // Delay time
|
||||
int i; // Position in circular buffer
|
||||
int wi; // Write index for delay queue
|
||||
int ri; // Read index for delay queue
|
||||
}af_surround_t;
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
af_surround_t *s = af->priv;
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:{
|
||||
struct mp_audio *in = arg;
|
||||
float fc;
|
||||
if (!mp_chmap_is_stereo(&in->channels)) {
|
||||
MP_ERR(af, "Only stereo input is supported.\n");
|
||||
return AF_DETACH;
|
||||
}
|
||||
|
||||
mp_audio_set_format(in, AF_FORMAT_FLOAT);
|
||||
mp_audio_copy_config(af->data, in);
|
||||
mp_audio_set_channels_old(af->data, in->nch * 2);
|
||||
|
||||
// Surround filer coefficients
|
||||
fc = 2.0 * 7000.0/(float)af->data->rate;
|
||||
if (-1 == af_filter_design_fir(L, s->w, &fc, LP|HAMMING, 0)){
|
||||
MP_ERR(af, "Unable to design low-pass filter.\n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
// Free previous delay queues
|
||||
free(s->dl);
|
||||
free(s->dr);
|
||||
// Allocate new delay queues
|
||||
s->dl = calloc(LD,af->data->bps);
|
||||
s->dr = calloc(LD,af->data->bps);
|
||||
if((NULL == s->dl) || (NULL == s->dr))
|
||||
MP_FATAL(af, "Out of memory\n");
|
||||
|
||||
// Initialize delay queue index
|
||||
if(AF_OK != af_from_ms(1, &s->d, &s->wi, af->data->rate, 0.0, 1000.0))
|
||||
return AF_ERROR;
|
||||
// printf("%i\n",s->wi);
|
||||
s->ri = 0;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
// The beginnings of an active matrix...
|
||||
static const float steering_matrix[][12] = {
|
||||
// LL RL LR RR LS RS
|
||||
// LLs RLs LRs RRs LC RC
|
||||
{.707, .0, .0, .707, .5, -.5,
|
||||
.5878, -.3928, .3928, -.5878, .5, .5},
|
||||
};
|
||||
|
||||
// Experimental moving average dominance
|
||||
//static int amp_L = 0, amp_R = 0, amp_C = 0, amp_S = 0;
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
struct mp_audio *outframe =
|
||||
mp_audio_pool_get(af->out_pool, &af->fmt_out, data->samples);
|
||||
if (!outframe) {
|
||||
talloc_free(data);
|
||||
return -1;
|
||||
}
|
||||
mp_audio_copy_attributes(outframe, data);
|
||||
|
||||
af_surround_t* s = (af_surround_t*)af->priv;
|
||||
const float* m = steering_matrix[0];
|
||||
float* in = data->planes[0]; // Input audio data
|
||||
float* out = outframe->planes[0]; // Output audio data
|
||||
float* end = in + data->samples * data->nch;
|
||||
int i = s->i; // Filter queue index
|
||||
int ri = s->ri; // Read index for delay queue
|
||||
int wi = s->wi; // Write index for delay queue
|
||||
|
||||
while(in < end){
|
||||
/* Dominance:
|
||||
abs(in[0]) abs(in[1]);
|
||||
abs(in[0]+in[1]) abs(in[0]-in[1]);
|
||||
10 * log( abs(in[0]) / (abs(in[1])|1) );
|
||||
10 * log( abs(in[0]+in[1]) / (abs(in[0]-in[1])|1) ); */
|
||||
|
||||
/* About volume balancing...
|
||||
Surround encoding does the following:
|
||||
Lt=L+.707*C+.707*S, Rt=R+.707*C-.707*S
|
||||
So S should be extracted as:
|
||||
(Lt-Rt)
|
||||
But we are splitting the S to two output channels, so we
|
||||
must take 3dB off as we split it:
|
||||
Ls=Rs=.707*(Lt-Rt)
|
||||
Trouble is, Lt could be +1, Rt -1, so possibility that S will
|
||||
overflow. So to avoid that, we cut L/R by 3dB (*.707), and S by
|
||||
6dB (/2). This keeps the overall balance, but guarantees no
|
||||
overflow. */
|
||||
|
||||
// Output front left and right
|
||||
out[0] = m[0]*in[0] + m[1]*in[1];
|
||||
out[1] = m[2]*in[0] + m[3]*in[1];
|
||||
|
||||
// Low-pass output @ 7kHz
|
||||
FIR((&s->lq[i]), s->w, s->dl[wi]);
|
||||
|
||||
// Delay output by d ms
|
||||
out[2] = s->dl[ri];
|
||||
|
||||
#ifdef SPLITREAR
|
||||
// Low-pass output @ 7kHz
|
||||
FIR((&s->rq[i]), s->w, s->dr[wi]);
|
||||
|
||||
// Delay output by d ms
|
||||
out[3] = s->dr[ri];
|
||||
#else
|
||||
out[3] = -out[2];
|
||||
#endif
|
||||
|
||||
// Update delay queues indexes
|
||||
UPDATEQI(ri);
|
||||
UPDATEQI(wi);
|
||||
|
||||
// Calculate and save surround in circular queue
|
||||
#ifdef SPLITREAR
|
||||
ADDQUE(i, s->rq, s->lq, m[6]*in[0]+m[7]*in[1], m[8]*in[0]+m[9]*in[1]);
|
||||
#else
|
||||
ADDQUE(i, s->lq, m[4]*in[0]+m[5]*in[1]);
|
||||
#endif
|
||||
|
||||
// Next sample...
|
||||
in = &in[data->nch];
|
||||
out = &out[af->data->nch];
|
||||
}
|
||||
|
||||
// Save indexes
|
||||
s->i = i; s->ri = ri; s->wi = wi;
|
||||
|
||||
talloc_free(data);
|
||||
af_add_output_frame(af, outframe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = filter_frame;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_surround_t
|
||||
const struct af_info af_info_surround =
|
||||
{
|
||||
.info = "Surround decoder filter",
|
||||
.name = "surround",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_surround_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLOATRANGE("d", d, 0, 0, 1000, OPTDEF_FLOAT(20.0)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "af.h"
|
||||
|
||||
typedef struct af_sweep_s{
|
||||
double x;
|
||||
double delta;
|
||||
}af_sweept;
|
||||
|
||||
|
||||
// Initialization and runtime control
|
||||
static int control(struct af_instance* af, int cmd, void* arg)
|
||||
{
|
||||
struct mp_audio *data= (struct mp_audio*)arg;
|
||||
|
||||
switch(cmd){
|
||||
case AF_CONTROL_REINIT:
|
||||
mp_audio_copy_config(af->data, data);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16);
|
||||
|
||||
return af_test_output(af, data);
|
||||
}
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static int filter_frame(struct af_instance *af, struct mp_audio *data)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
if (af_make_writeable(af, data) < 0) {
|
||||
talloc_free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
af_sweept *s = af->priv;
|
||||
int i, j;
|
||||
int16_t *in = (int16_t*)data->planes[0];
|
||||
int chans = data->nch;
|
||||
int in_len = data->samples;
|
||||
|
||||
for(i=0; i<in_len; i++){
|
||||
for(j=0; j<chans; j++)
|
||||
in[i*chans+j]= 32000*sin(s->x*s->x);
|
||||
s->x += s->delta;
|
||||
if(2*s->x*s->delta >= 3.141592) s->x=0;
|
||||
}
|
||||
|
||||
af_add_output_frame(af, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int af_open(struct af_instance* af){
|
||||
af->control=control;
|
||||
af->filter_frame = filter_frame;
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT af_sweept
|
||||
const struct af_info af_info_sweep = {
|
||||
.info = "sine sweep",
|
||||
.name = "sweep",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_sweept),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_DOUBLE("delta", delta, 0),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -168,7 +168,6 @@ static int af_open(struct af_instance *af)
|
||||
const struct af_info af_info_volume = {
|
||||
.info = "Volume control audio filter",
|
||||
.name = "volume",
|
||||
.flags = AF_FLAGS_NOT_REENTRANT,
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]) {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_DSP_H
|
||||
#define MPLAYER_DSP_H
|
||||
|
||||
/* Implementation of routines used for DSP */
|
||||
|
||||
/* Size of floating point type used in routines */
|
||||
#define FLOAT_TYPE float
|
||||
|
||||
#include "window.h"
|
||||
#include "filter.h"
|
||||
|
||||
#endif /* MPLAYER_DSP_H */
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
* design and implementation of different types of digital filters
|
||||
*
|
||||
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "dsp.h"
|
||||
|
||||
/******************************************************************************
|
||||
* FIR filter implementations
|
||||
******************************************************************************/
|
||||
|
||||
/* C implementation of FIR filter y=w*x
|
||||
|
||||
n number of filter taps, where mod(n,4)==0
|
||||
w filter taps
|
||||
x input signal must be a circular buffer which is indexed backwards
|
||||
*/
|
||||
inline FLOAT_TYPE af_filter_fir(register unsigned int n, const FLOAT_TYPE* w,
|
||||
const FLOAT_TYPE* x)
|
||||
{
|
||||
register FLOAT_TYPE y; // Output
|
||||
y = 0.0;
|
||||
do{
|
||||
n--;
|
||||
y+=w[n]*x[n];
|
||||
}while(n != 0);
|
||||
return y;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FIR filter design
|
||||
******************************************************************************/
|
||||
|
||||
/* Design FIR filter using the Window method
|
||||
|
||||
n filter length must be odd for HP and BS filters
|
||||
w buffer for the filter taps (must be n long)
|
||||
fc cutoff frequencies (1 for LP and HP, 2 for BP and BS)
|
||||
0 < fc < 1 where 1 <=> Fs/2
|
||||
flags window and filter type as defined in filter.h
|
||||
variables are ored together: i.e. LP|HAMMING will give a
|
||||
low pass filter designed using a hamming window
|
||||
opt beta constant used only when designing using kaiser windows
|
||||
|
||||
returns 0 if OK, -1 if fail
|
||||
*/
|
||||
int af_filter_design_fir(unsigned int n, FLOAT_TYPE* w, const FLOAT_TYPE* fc,
|
||||
unsigned int flags, FLOAT_TYPE opt)
|
||||
{
|
||||
unsigned int o = n & 1; // Indicator for odd filter length
|
||||
unsigned int end = ((n + 1) >> 1) - o; // Loop end
|
||||
unsigned int i; // Loop index
|
||||
|
||||
FLOAT_TYPE k1 = 2 * M_PI; // 2*pi*fc1
|
||||
FLOAT_TYPE k2 = 0.5 * (FLOAT_TYPE)(1 - o);// Constant used if the filter has even length
|
||||
FLOAT_TYPE k3; // 2*pi*fc2 Constant used in BP and BS design
|
||||
FLOAT_TYPE g = 0.0; // Gain
|
||||
FLOAT_TYPE t1,t2,t3; // Temporary variables
|
||||
FLOAT_TYPE fc1,fc2; // Cutoff frequencies
|
||||
|
||||
// Sanity check
|
||||
if(!w || (n == 0)) return -1;
|
||||
|
||||
// Get window coefficients
|
||||
switch(flags & WINDOW_MASK){
|
||||
case(BOXCAR):
|
||||
af_window_boxcar(n,w); break;
|
||||
case(TRIANG):
|
||||
af_window_triang(n,w); break;
|
||||
case(HAMMING):
|
||||
af_window_hamming(n,w); break;
|
||||
case(HANNING):
|
||||
af_window_hanning(n,w); break;
|
||||
case(BLACKMAN):
|
||||
af_window_blackman(n,w); break;
|
||||
case(FLATTOP):
|
||||
af_window_flattop(n,w); break;
|
||||
case(KAISER):
|
||||
af_window_kaiser(n,w,opt); break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(flags & (LP | HP)){
|
||||
fc1=*fc;
|
||||
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
|
||||
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25;
|
||||
k1 *= fc1;
|
||||
|
||||
if(flags & LP){ // Low pass filter
|
||||
|
||||
// If the filter length is odd, there is one point which is exactly
|
||||
// in the middle. The value at this point is 2*fCutoff*sin(x)/x,
|
||||
// where x is zero. To make sure nothing strange happens, we set this
|
||||
// value separately.
|
||||
if (o){
|
||||
w[end] = fc1 * w[end] * 2.0;
|
||||
g=w[end];
|
||||
}
|
||||
|
||||
// Create filter
|
||||
for (i=0 ; i<end ; i++){
|
||||
t1 = (FLOAT_TYPE)(i+1) - k2;
|
||||
w[end-i-1] = w[n-end+i] = w[end-i-1] * sin(k1 * t1)/(M_PI * t1); // Sinc
|
||||
g += 2*w[end-i-1]; // Total gain in filter
|
||||
}
|
||||
}
|
||||
else{ // High pass filter
|
||||
if (!o) // High pass filters must have odd length
|
||||
return -1;
|
||||
w[end] = 1.0 - (fc1 * w[end] * 2.0);
|
||||
g= w[end];
|
||||
|
||||
// Create filter
|
||||
for (i=0 ; i<end ; i++){
|
||||
t1 = (FLOAT_TYPE)(i+1);
|
||||
w[end-i-1] = w[n-end+i] = -1 * w[end-i-1] * sin(k1 * t1)/(M_PI * t1); // Sinc
|
||||
g += ((i&1) ? (2*w[end-i-1]) : (-2*w[end-i-1])); // Total gain in filter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(flags & (BP | BS)){
|
||||
fc1=fc[0];
|
||||
fc2=fc[1];
|
||||
// Cutoff frequencies must be < 1.0 where 1.0 <=> Fs/2
|
||||
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1/2 : 0.25;
|
||||
fc2 = ((fc2 <= 1.0) && (fc2 > 0.0)) ? fc2/2 : 0.25;
|
||||
k3 = k1 * fc2; // 2*pi*fc2
|
||||
k1 *= fc1; // 2*pi*fc1
|
||||
|
||||
if(flags & BP){ // Band pass
|
||||
// Calculate center tap
|
||||
if (o){
|
||||
g=w[end]*(fc1+fc2);
|
||||
w[end] = (fc2 - fc1) * w[end] * 2.0;
|
||||
}
|
||||
|
||||
// Create filter
|
||||
for (i=0 ; i<end ; i++){
|
||||
t1 = (FLOAT_TYPE)(i+1) - k2;
|
||||
t2 = sin(k3 * t1)/(M_PI * t1); // Sinc fc2
|
||||
t3 = sin(k1 * t1)/(M_PI * t1); // Sinc fc1
|
||||
g += w[end-i-1] * (t3 + t2); // Total gain in filter
|
||||
w[end-i-1] = w[n-end+i] = w[end-i-1] * (t2 - t3);
|
||||
}
|
||||
}
|
||||
else{ // Band stop
|
||||
if (!o) // Band stop filters must have odd length
|
||||
return -1;
|
||||
w[end] = 1.0 - (fc2 - fc1) * w[end] * 2.0;
|
||||
g= w[end];
|
||||
|
||||
// Create filter
|
||||
for (i=0 ; i<end ; i++){
|
||||
t1 = (FLOAT_TYPE)(i+1);
|
||||
t2 = sin(k1 * t1)/(M_PI * t1); // Sinc fc1
|
||||
t3 = sin(k3 * t1)/(M_PI * t1); // Sinc fc2
|
||||
w[end-i-1] = w[n-end+i] = w[end-i-1] * (t2 - t3);
|
||||
g += 2*w[end-i-1]; // Total gain in filter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize gain
|
||||
g=1/g;
|
||||
for (i=0; i<n; i++)
|
||||
w[i] *= g;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* IIR filter design
|
||||
******************************************************************************/
|
||||
|
||||
/* Helper functions for the bilinear transform */
|
||||
|
||||
/* Pre-warp the coefficients of a numerator or denominator.
|
||||
Note that a0 is assumed to be 1, so there is no wrapping
|
||||
of it.
|
||||
*/
|
||||
static void af_filter_prewarp(FLOAT_TYPE* a, FLOAT_TYPE fc, FLOAT_TYPE fs)
|
||||
{
|
||||
FLOAT_TYPE wp;
|
||||
wp = 2.0 * fs * tan(M_PI * fc / fs);
|
||||
a[2] = a[2]/(wp * wp);
|
||||
a[1] = a[1]/wp;
|
||||
}
|
||||
|
||||
/* Transform the numerator and denominator coefficients of s-domain
|
||||
biquad section into corresponding z-domain coefficients.
|
||||
|
||||
The transfer function for z-domain is:
|
||||
|
||||
1 + alpha1 * z^(-1) + alpha2 * z^(-2)
|
||||
H(z) = -------------------------------------
|
||||
1 + beta1 * z^(-1) + beta2 * z^(-2)
|
||||
|
||||
Store the 4 IIR coefficients in array pointed by coef in following
|
||||
order:
|
||||
beta1, beta2 (denominator)
|
||||
alpha1, alpha2 (numerator)
|
||||
|
||||
Arguments:
|
||||
a - s-domain numerator coefficients
|
||||
b - s-domain denominator coefficients
|
||||
k - filter gain factor. Initially set to 1 and modified by each
|
||||
biquad section in such a way, as to make it the
|
||||
coefficient by which to multiply the overall filter gain
|
||||
in order to achieve a desired overall filter gain,
|
||||
specified in initial value of k.
|
||||
fs - sampling rate (Hz)
|
||||
coef - array of z-domain coefficients to be filled in.
|
||||
|
||||
Return: On return, set coef z-domain coefficients and k to the gain
|
||||
required to maintain overall gain = 1.0;
|
||||
*/
|
||||
static void af_filter_bilinear(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE* k,
|
||||
FLOAT_TYPE fs, FLOAT_TYPE *coef)
|
||||
{
|
||||
FLOAT_TYPE ad, bd;
|
||||
|
||||
/* alpha (Numerator in s-domain) */
|
||||
ad = 4. * a[2] * fs * fs + 2. * a[1] * fs + a[0];
|
||||
/* beta (Denominator in s-domain) */
|
||||
bd = 4. * b[2] * fs * fs + 2. * b[1] * fs + b[0];
|
||||
|
||||
/* Update gain constant for this section */
|
||||
*k *= ad/bd;
|
||||
|
||||
/* Denominator */
|
||||
*coef++ = (2. * b[0] - 8. * b[2] * fs * fs)/bd; /* beta1 */
|
||||
*coef++ = (4. * b[2] * fs * fs - 2. * b[1] * fs + b[0])/bd; /* beta2 */
|
||||
|
||||
/* Numerator */
|
||||
*coef++ = (2. * a[0] - 8. * a[2] * fs * fs)/ad; /* alpha1 */
|
||||
*coef = (4. * a[2] * fs * fs - 2. * a[1] * fs + a[0])/ad; /* alpha2 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* IIR filter design using bilinear transform and prewarp. Transforms
|
||||
2nd order s domain analog filter into a digital IIR biquad link. To
|
||||
create a filter fill in a, b, Q and fs and make space for coef and k.
|
||||
|
||||
|
||||
Example Butterworth design:
|
||||
|
||||
Below are Butterworth polynomials, arranged as a series of 2nd
|
||||
order sections:
|
||||
|
||||
Note: n is filter order.
|
||||
|
||||
n Polynomials
|
||||
-------------------------------------------------------------------
|
||||
2 s^2 + 1.4142s + 1
|
||||
4 (s^2 + 0.765367s + 1) * (s^2 + 1.847759s + 1)
|
||||
6 (s^2 + 0.5176387s + 1) * (s^2 + 1.414214 + 1) * (s^2 + 1.931852s + 1)
|
||||
|
||||
For n=4 we have following equation for the filter transfer function:
|
||||
1 1
|
||||
T(s) = --------------------------- * ----------------------------
|
||||
s^2 + (1/Q) * 0.765367s + 1 s^2 + (1/Q) * 1.847759s + 1
|
||||
|
||||
The filter consists of two 2nd order sections since highest s power
|
||||
is 2. Now we can take the coefficients, or the numbers by which s
|
||||
is multiplied and plug them into a standard formula to be used by
|
||||
bilinear transform.
|
||||
|
||||
Our standard form for each 2nd order section is:
|
||||
|
||||
a2 * s^2 + a1 * s + a0
|
||||
H(s) = ----------------------
|
||||
b2 * s^2 + b1 * s + b0
|
||||
|
||||
Note that Butterworth numerator is 1 for all filter sections, which
|
||||
means s^2 = 0 and s^1 = 0
|
||||
|
||||
Let's convert standard Butterworth polynomials into this form:
|
||||
|
||||
0 + 0 + 1 0 + 0 + 1
|
||||
--------------------------- * --------------------------
|
||||
1 + ((1/Q) * 0.765367) + 1 1 + ((1/Q) * 1.847759) + 1
|
||||
|
||||
Section 1:
|
||||
a2 = 0; a1 = 0; a0 = 1;
|
||||
b2 = 1; b1 = 0.765367; b0 = 1;
|
||||
|
||||
Section 2:
|
||||
a2 = 0; a1 = 0; a0 = 1;
|
||||
b2 = 1; b1 = 1.847759; b0 = 1;
|
||||
|
||||
Q is filter quality factor or resonance, in the range of 1 to
|
||||
1000. The overall filter Q is a product of all 2nd order stages.
|
||||
For example, the 6th order filter (3 stages, or biquads) with
|
||||
individual Q of 2 will have filter Q = 2 * 2 * 2 = 8.
|
||||
|
||||
|
||||
Arguments:
|
||||
a - s-domain numerator coefficients, a[1] is always assumed to be 1.0
|
||||
b - s-domain denominator coefficients
|
||||
Q - Q value for the filter
|
||||
k - filter gain factor. Initially set to 1 and modified by each
|
||||
biquad section in such a way, as to make it the
|
||||
coefficient by which to multiply the overall filter gain
|
||||
in order to achieve a desired overall filter gain,
|
||||
specified in initial value of k.
|
||||
fs - sampling rate (Hz)
|
||||
coef - array of z-domain coefficients to be filled in.
|
||||
|
||||
Note: Upon return from each call, the k argument will be set to a
|
||||
value, by which to multiply our actual signal in order for the gain
|
||||
to be one. On second call to szxform() we provide k that was
|
||||
changed by the previous section. During actual audio filtering
|
||||
k can be used for gain compensation.
|
||||
|
||||
return -1 if fail 0 if success.
|
||||
*/
|
||||
int af_filter_szxform(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE Q, FLOAT_TYPE fc,
|
||||
FLOAT_TYPE fs, FLOAT_TYPE *k, FLOAT_TYPE *coef)
|
||||
{
|
||||
FLOAT_TYPE at[3];
|
||||
FLOAT_TYPE bt[3];
|
||||
|
||||
if(!a || !b || !k || !coef || (Q>1000.0 || Q< 1.0))
|
||||
return -1;
|
||||
|
||||
memcpy(at,a,3*sizeof(FLOAT_TYPE));
|
||||
memcpy(bt,b,3*sizeof(FLOAT_TYPE));
|
||||
|
||||
bt[1]/=Q;
|
||||
|
||||
/* Calculate a and b and overwrite the original values */
|
||||
af_filter_prewarp(at, fc, fs);
|
||||
af_filter_prewarp(bt, fc, fs);
|
||||
/* Execute bilinear transform */
|
||||
af_filter_bilinear(at, bt, k, fs, coef);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined MPLAYER_DSP_H
|
||||
# error Never use filter.h directly; include dsp.h instead.
|
||||
#endif
|
||||
|
||||
#ifndef MPLAYER_FILTER_H
|
||||
#define MPLAYER_FILTER_H
|
||||
|
||||
|
||||
// Design and implementation of different types of digital filters
|
||||
|
||||
|
||||
// Flags used for filter design
|
||||
|
||||
// Filter characteristics
|
||||
#define LP 0x00010000 // Low pass
|
||||
#define HP 0x00020000 // High pass
|
||||
#define BP 0x00040000 // Band pass
|
||||
#define BS 0x00080000 // Band stop
|
||||
#define TYPE_MASK 0x000F0000
|
||||
|
||||
// Window types
|
||||
#define BOXCAR 0x00000001
|
||||
#define TRIANG 0x00000002
|
||||
#define HAMMING 0x00000004
|
||||
#define HANNING 0x00000008
|
||||
#define BLACKMAN 0x00000010
|
||||
#define FLATTOP 0x00000011
|
||||
#define KAISER 0x00000012
|
||||
#define WINDOW_MASK 0x0000001F
|
||||
|
||||
// Parallel filter design
|
||||
#define FWD 0x00000001 // Forward indexing of polyphase filter
|
||||
#define REW 0x00000002 // Reverse indexing of polyphase filter
|
||||
#define ODD 0x00000010 // Make filter HP
|
||||
|
||||
// Exported functions
|
||||
FLOAT_TYPE af_filter_fir(unsigned int n, const FLOAT_TYPE* w, const FLOAT_TYPE* x);
|
||||
|
||||
int af_filter_design_fir(unsigned int n, FLOAT_TYPE* w, const FLOAT_TYPE* fc,
|
||||
unsigned int flags, FLOAT_TYPE opt);
|
||||
|
||||
int af_filter_szxform(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE Q,
|
||||
FLOAT_TYPE fc, FLOAT_TYPE fs, FLOAT_TYPE *k,
|
||||
FLOAT_TYPE *coef);
|
||||
|
||||
/* Add new data to circular queue designed to be used with a FIR
|
||||
filter. xq is the circular queue, in pointing at the new sample, xi
|
||||
current index for xq and n the length of the filter. xq must be n*2
|
||||
long.
|
||||
*/
|
||||
#define af_filter_updateq(n,xi,xq,in)\
|
||||
xq[xi]=(xq)[(xi)+(n)]=*(in);\
|
||||
xi=(++(xi))&((n)-1);
|
||||
|
||||
#endif /* MPLAYER_FILTER_H */
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Calculates a number of window functions. The following window
|
||||
functions are currently implemented: Boxcar, Triang, Hanning,
|
||||
Hamming, Blackman, Flattop and Kaiser. In the function call n is
|
||||
the number of filter taps and w the buffer in which the filter
|
||||
coefficients will be stored.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "dsp.h"
|
||||
|
||||
/*
|
||||
// Boxcar
|
||||
//
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
*/
|
||||
void af_window_boxcar(int n, FLOAT_TYPE* w)
|
||||
{
|
||||
int i;
|
||||
// Calculate window coefficients
|
||||
for (i=0 ; i<n ; i++)
|
||||
w[i] = 1.0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Triang a.k.a Bartlett
|
||||
//
|
||||
// | (N-1)|
|
||||
// 2 * |k - -----|
|
||||
// | 2 |
|
||||
// w = 1.0 - ---------------
|
||||
// N+1
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
*/
|
||||
void af_window_triang(int n, FLOAT_TYPE* w)
|
||||
{
|
||||
FLOAT_TYPE k1 = (FLOAT_TYPE)(n & 1);
|
||||
FLOAT_TYPE k2 = 1/((FLOAT_TYPE)n + k1);
|
||||
int end = (n + 1) >> 1;
|
||||
int i;
|
||||
|
||||
// Calculate window coefficients
|
||||
for (i=0 ; i<end ; i++)
|
||||
w[i] = w[n-i-1] = (2.0*((FLOAT_TYPE)(i+1))-(1.0-k1))*k2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Hanning
|
||||
// 2*pi*k
|
||||
// w = 0.5 - 0.5*cos(------), where 0 < k <= N
|
||||
// N+1
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
*/
|
||||
void af_window_hanning(int n, FLOAT_TYPE* w)
|
||||
{
|
||||
int i;
|
||||
FLOAT_TYPE k = 2*M_PI/((FLOAT_TYPE)(n+1)); // 2*pi/(N+1)
|
||||
|
||||
// Calculate window coefficients
|
||||
for (i=0; i<n; i++)
|
||||
*w++ = 0.5*(1.0 - cos(k*(FLOAT_TYPE)(i+1)));
|
||||
}
|
||||
|
||||
/*
|
||||
// Hamming
|
||||
// 2*pi*k
|
||||
// w(k) = 0.54 - 0.46*cos(------), where 0 <= k < N
|
||||
// N-1
|
||||
//
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
*/
|
||||
void af_window_hamming(int n,FLOAT_TYPE* w)
|
||||
{
|
||||
int i;
|
||||
FLOAT_TYPE k = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1)
|
||||
|
||||
// Calculate window coefficients
|
||||
for (i=0; i<n; i++)
|
||||
*w++ = 0.54 - 0.46*cos(k*(FLOAT_TYPE)i);
|
||||
}
|
||||
|
||||
/*
|
||||
// Blackman
|
||||
// 2*pi*k 4*pi*k
|
||||
// w(k) = 0.42 - 0.5*cos(------) + 0.08*cos(------), where 0 <= k < N
|
||||
// N-1 N-1
|
||||
//
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
*/
|
||||
void af_window_blackman(int n,FLOAT_TYPE* w)
|
||||
{
|
||||
int i;
|
||||
FLOAT_TYPE k1 = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1)
|
||||
FLOAT_TYPE k2 = 2*k1; // 4*pi/(N-1)
|
||||
|
||||
// Calculate window coefficients
|
||||
for (i=0; i<n; i++)
|
||||
*w++ = 0.42 - 0.50*cos(k1*(FLOAT_TYPE)i) + 0.08*cos(k2*(FLOAT_TYPE)i);
|
||||
}
|
||||
|
||||
/*
|
||||
// Flattop
|
||||
// 2*pi*k 4*pi*k
|
||||
// w(k) = 0.2810638602 - 0.5208971735*cos(------) + 0.1980389663*cos(------), where 0 <= k < N
|
||||
// N-1 N-1
|
||||
//
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
*/
|
||||
void af_window_flattop(int n,FLOAT_TYPE* w)
|
||||
{
|
||||
int i;
|
||||
FLOAT_TYPE k1 = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1)
|
||||
FLOAT_TYPE k2 = 2*k1; // 4*pi/(N-1)
|
||||
|
||||
// Calculate window coefficients
|
||||
for (i=0; i<n; i++)
|
||||
*w++ = 0.2810638602 - 0.5208971735*cos(k1*(FLOAT_TYPE)i)
|
||||
+ 0.1980389663*cos(k2*(FLOAT_TYPE)i);
|
||||
}
|
||||
|
||||
/* Computes the 0th order modified Bessel function of the first kind.
|
||||
// (Needed to compute Kaiser window)
|
||||
//
|
||||
// y = sum( (x/(2*n))^2 )
|
||||
// n
|
||||
*/
|
||||
#define BIZ_EPSILON 1E-21 // Max error acceptable
|
||||
|
||||
static FLOAT_TYPE besselizero(FLOAT_TYPE x)
|
||||
{
|
||||
FLOAT_TYPE temp;
|
||||
FLOAT_TYPE sum = 1.0;
|
||||
FLOAT_TYPE u = 1.0;
|
||||
FLOAT_TYPE halfx = x/2.0;
|
||||
int n = 1;
|
||||
|
||||
do {
|
||||
temp = halfx/(FLOAT_TYPE)n;
|
||||
u *=temp * temp;
|
||||
sum += u;
|
||||
n++;
|
||||
} while (u >= BIZ_EPSILON * sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
// Kaiser
|
||||
//
|
||||
// n window length
|
||||
// w buffer for the window parameters
|
||||
// b beta parameter of Kaiser window, Beta >= 1
|
||||
//
|
||||
// Beta trades the rejection of the low pass filter against the
|
||||
// transition width from passband to stop band. Larger Beta means a
|
||||
// slower transition and greater stop band rejection. See Rabiner and
|
||||
// Gold (Theory and Application of DSP) under Kaiser windows for more
|
||||
// about Beta. The following table from Rabiner and Gold gives some
|
||||
// feel for the effect of Beta:
|
||||
//
|
||||
// All ripples in dB, width of transition band = D*N where N = window
|
||||
// length
|
||||
//
|
||||
// BETA D PB RIP SB RIP
|
||||
// 2.120 1.50 +-0.27 -30
|
||||
// 3.384 2.23 0.0864 -40
|
||||
// 4.538 2.93 0.0274 -50
|
||||
// 5.658 3.62 0.00868 -60
|
||||
// 6.764 4.32 0.00275 -70
|
||||
// 7.865 5.0 0.000868 -80
|
||||
// 8.960 5.7 0.000275 -90
|
||||
// 10.056 6.4 0.000087 -100
|
||||
*/
|
||||
void af_window_kaiser(int n, FLOAT_TYPE* w, FLOAT_TYPE b)
|
||||
{
|
||||
FLOAT_TYPE tmp;
|
||||
FLOAT_TYPE k1 = 1.0/besselizero(b);
|
||||
int k2 = 1 - (n & 1);
|
||||
int end = (n + 1) >> 1;
|
||||
int i;
|
||||
|
||||
// Calculate window coefficients
|
||||
for (i=0 ; i<end ; i++){
|
||||
tmp = (FLOAT_TYPE)(2*i + k2) / ((FLOAT_TYPE)n - 1.0);
|
||||
w[end-(1&(!k2))+i] = w[end-1-i] = k1 * besselizero(b*sqrt(1.0 - tmp*tmp));
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Calculates a number of window functions. The following window
|
||||
functions are currently implemented: Boxcar, Triang, Hanning,
|
||||
Hamming, Blackman, Flattop and Kaiser. In the function call n is
|
||||
the number of filter taps and w the buffer in which the filter
|
||||
coefficients will be stored.
|
||||
*/
|
||||
|
||||
#if !defined MPLAYER_DSP_H
|
||||
# error Never use window.h directly; include dsp.h instead.
|
||||
#endif
|
||||
|
||||
#ifndef MPLAYER_WINDOW_H
|
||||
#define MPLAYER_WINDOW_H
|
||||
|
||||
void af_window_boxcar(int n, FLOAT_TYPE* w);
|
||||
void af_window_triang(int n, FLOAT_TYPE* w);
|
||||
void af_window_hanning(int n, FLOAT_TYPE* w);
|
||||
void af_window_hamming(int n, FLOAT_TYPE* w);
|
||||
void af_window_blackman(int n, FLOAT_TYPE* w);
|
||||
void af_window_flattop(int n, FLOAT_TYPE* w);
|
||||
void af_window_kaiser(int n, FLOAT_TYPE* w, FLOAT_TYPE b);
|
||||
|
||||
#endif /* MPLAYER_WINDOW_H */
|
||||
+45
-11
@@ -171,13 +171,11 @@ void af_fill_silence(void *dst, size_t bytes, int format)
|
||||
memset(dst, af_fmt_is_unsigned(format) ? 0x80 : 0, bytes);
|
||||
}
|
||||
|
||||
#define FMT_DIFF(type, a, b) (((a) & type) - ((b) & type))
|
||||
|
||||
// Returns a "score" that serves as heuristic how lossy or hard a conversion is.
|
||||
// If the formats are equal, 1024 is returned. If they are gravely incompatible
|
||||
// (like s16<->ac3), INT_MIN is returned. If there is implied loss of precision
|
||||
// (like s16->s8), a value <0 is returned.
|
||||
int af_format_conversion_score(int dst_format, int src_format)
|
||||
static int af_format_conversion_score(int dst_format, int src_format)
|
||||
{
|
||||
if (dst_format == AF_FORMAT_UNKNOWN || src_format == AF_FORMAT_UNKNOWN)
|
||||
return INT_MIN;
|
||||
@@ -192,25 +190,61 @@ int af_format_conversion_score(int dst_format, int src_format)
|
||||
if (af_fmt_is_float(dst_format) != af_fmt_is_float(src_format)) {
|
||||
int dst_bytes = af_fmt_to_bytes(dst_format);
|
||||
if (af_fmt_is_float(dst_format)) {
|
||||
// For int->float, always prefer 32 bit float.
|
||||
score -= dst_bytes == 4 ? 1 : 0;
|
||||
// For int->float, consider a lower bound on the precision difference.
|
||||
int bytes = (dst_bytes == 4 ? 3 : 6) - af_fmt_to_bytes(src_format);
|
||||
if (bytes >= 0) {
|
||||
score -= 8 * bytes; // excess precision
|
||||
} else {
|
||||
score += 1024 * (bytes - 1); // precision is lost (i.e. s32 -> float)
|
||||
}
|
||||
} else {
|
||||
// For float->int, always prefer highest bit depth int
|
||||
score -= 8 - dst_bytes;
|
||||
// float->int is the worst case. Penalize heavily and
|
||||
// prefer highest bit depth int.
|
||||
score -= 1048576 * (8 - dst_bytes);
|
||||
}
|
||||
// Has to convert float<->int - Consider this the worst case.
|
||||
score -= 2048;
|
||||
score -= 512; // penalty for any float <-> int conversion
|
||||
} else {
|
||||
int bytes = af_fmt_to_bytes(dst_format) - af_fmt_to_bytes(src_format);
|
||||
if (bytes > 0) {
|
||||
score -= bytes; // has to add padding
|
||||
score -= 8 * bytes; // has to add padding
|
||||
} else if (bytes < 0) {
|
||||
score -= 1024 - bytes; // has to reduce bit depth
|
||||
score += 1024 * (bytes - 1); // has to reduce bit depth
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
struct entry {
|
||||
int fmt;
|
||||
int score;
|
||||
};
|
||||
|
||||
static int cmp_entry(const void *a, const void *b)
|
||||
{
|
||||
#define CMP_INT(a, b) (a > b ? 1 : (a < b ? -1 : 0))
|
||||
return -CMP_INT(((struct entry *)a)->score, ((struct entry *)b)->score);
|
||||
}
|
||||
|
||||
// Return a list of sample format compatible to src_format, sorted by order
|
||||
// of preference. out_formats[0] will be src_format (as long as it's valid),
|
||||
// and the list is terminated with 0 (AF_FORMAT_UNKNOWN).
|
||||
// Keep in mind that this also returns formats with flipped interleaving
|
||||
// (e.g. for s16, it returns [s16, s16p, ...]).
|
||||
void af_get_best_sample_formats(int src_format, int out_formats[AF_FORMAT_COUNT])
|
||||
{
|
||||
int num = 0;
|
||||
struct entry e[AF_FORMAT_COUNT];
|
||||
for (int fmt = 1; fmt < AF_FORMAT_COUNT; fmt++) {
|
||||
int score = af_format_conversion_score(fmt, src_format);
|
||||
if (score > INT_MIN)
|
||||
e[num++] = (struct entry){fmt, score};
|
||||
}
|
||||
qsort(e, num, sizeof(e[0]), cmp_entry);
|
||||
for (int n = 0; n < num; n++)
|
||||
out_formats[n] = e[n].fmt;
|
||||
out_formats[num] = 0;
|
||||
}
|
||||
|
||||
// Return the number of samples that make up one frame in this format.
|
||||
// You get the byte size by multiplying them with sample size and channel count.
|
||||
int af_format_sample_alignment(int format)
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int sampler
|
||||
|
||||
void af_fill_silence(void *dst, size_t bytes, int format);
|
||||
|
||||
int af_format_conversion_score(int dst_format, int src_format);
|
||||
void af_get_best_sample_formats(int src_format, int out_formats[AF_FORMAT_COUNT]);
|
||||
|
||||
int af_format_sample_alignment(int format);
|
||||
|
||||
|
||||
+52
-49
@@ -240,6 +240,7 @@ static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev)
|
||||
}
|
||||
|
||||
struct ao *ao_init_best(struct mpv_global *global,
|
||||
bool ao_null_fallback,
|
||||
struct input_ctx *input_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx,
|
||||
int samplerate, int format, struct mp_chmap channels)
|
||||
@@ -248,58 +249,67 @@ struct ao *ao_init_best(struct mpv_global *global,
|
||||
void *tmp = talloc_new(NULL);
|
||||
struct mp_log *log = mp_log_new(tmp, global->log, "ao");
|
||||
struct ao *ao = NULL;
|
||||
struct m_obj_settings *ao_list = opts->audio_driver_list;
|
||||
struct m_obj_settings *ao_list = NULL;
|
||||
int ao_num = 0;
|
||||
|
||||
for (int n = 0; opts->audio_driver_list && opts->audio_driver_list[n].name; n++)
|
||||
MP_TARRAY_APPEND(tmp, ao_list, ao_num, opts->audio_driver_list[n]);
|
||||
|
||||
bool forced_dev = false;
|
||||
char *pref_ao, *pref_dev;
|
||||
split_ao_device(tmp, opts->audio_device, &pref_ao, &pref_dev);
|
||||
if (!(ao_list && ao_list[0].name) && pref_ao) {
|
||||
if (!ao_num && pref_ao) {
|
||||
// Reuse the autoselection code
|
||||
ao_list = talloc_zero_array(tmp, struct m_obj_settings, 2);
|
||||
ao_list[0].name = pref_ao;
|
||||
MP_TARRAY_APPEND(tmp, ao_list, ao_num,
|
||||
(struct m_obj_settings){.name = pref_ao});
|
||||
forced_dev = true;
|
||||
}
|
||||
|
||||
if (ao_list && ao_list[0].name) {
|
||||
for (int n = 0; ao_list[n].name; n++) {
|
||||
if (strlen(ao_list[n].name) == 0)
|
||||
goto autoprobe;
|
||||
mp_verbose(log, "Trying preferred audio driver '%s'\n",
|
||||
ao_list[n].name);
|
||||
char *dev = NULL;
|
||||
if (pref_ao && strcmp(ao_list[n].name, pref_ao) == 0)
|
||||
dev = pref_dev;
|
||||
if (dev)
|
||||
mp_verbose(log, "Using preferred device '%s'\n", dev);
|
||||
ao = ao_init(false, global, input_ctx, encode_lavc_ctx,
|
||||
samplerate, format, channels, dev,
|
||||
ao_list[n].name, ao_list[n].attribs);
|
||||
if (ao)
|
||||
goto done;
|
||||
mp_err(log, "Failed to initialize audio driver '%s'\n",
|
||||
ao_list[n].name);
|
||||
if (forced_dev) {
|
||||
mp_err(log, "This audio driver/device was forced with the "
|
||||
"--audio-device option.\n"
|
||||
"Try unsetting it.\n");
|
||||
}
|
||||
bool autoprobe = ao_num == 0;
|
||||
|
||||
// Something like "--ao=a,b," means do autoprobing after a and b fail.
|
||||
if (ao_num && strlen(ao_list[ao_num - 1].name) == 0) {
|
||||
ao_num -= 1;
|
||||
autoprobe = true;
|
||||
}
|
||||
|
||||
if (autoprobe) {
|
||||
for (int n = 0; audio_out_drivers[n]; n++) {
|
||||
const struct ao_driver *driver = audio_out_drivers[n];
|
||||
if (driver == &audio_out_null)
|
||||
break;
|
||||
MP_TARRAY_APPEND(tmp, ao_list, ao_num,
|
||||
(struct m_obj_settings){.name = (char *)driver->name});
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
autoprobe: ;
|
||||
// now try the rest...
|
||||
for (int i = 0; audio_out_drivers[i]; i++) {
|
||||
const struct ao_driver *driver = audio_out_drivers[i];
|
||||
if (driver == &audio_out_null)
|
||||
break;
|
||||
ao = ao_init(true, global, input_ctx, encode_lavc_ctx, samplerate,
|
||||
format, channels, NULL, (char *)driver->name, NULL);
|
||||
if (ao_null_fallback) {
|
||||
MP_TARRAY_APPEND(tmp, ao_list, ao_num,
|
||||
(struct m_obj_settings){.name = "null"});
|
||||
}
|
||||
|
||||
for (int n = 0; n < ao_num; n++) {
|
||||
struct m_obj_settings *entry = &ao_list[n];
|
||||
bool probing = n + 1 != ao_num;
|
||||
mp_verbose(log, "Trying audio driver '%s'\n", entry->name);
|
||||
char *dev = NULL;
|
||||
if (pref_ao && pref_dev && strcmp(entry->name, pref_ao) == 0) {
|
||||
dev = pref_dev;
|
||||
mp_verbose(log, "Using preferred device '%s'\n", dev);
|
||||
}
|
||||
ao = ao_init(probing, global, input_ctx, encode_lavc_ctx,
|
||||
samplerate, format, channels, dev,
|
||||
entry->name, entry->attribs);
|
||||
if (ao)
|
||||
goto done;
|
||||
break;
|
||||
if (!probing)
|
||||
mp_err(log, "Failed to initialize audio driver '%s'\n", entry->name);
|
||||
if (dev && forced_dev) {
|
||||
mp_err(log, "This audio driver/device was forced with the "
|
||||
"--audio-device option.\nTry unsetting it.\n");
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
talloc_free(tmp);
|
||||
return ao;
|
||||
}
|
||||
@@ -411,18 +421,11 @@ void ao_hotplug_event(struct ao *ao)
|
||||
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
struct mp_chmap *map)
|
||||
{
|
||||
if (mp_msg_test(ao->log, MSGL_DEBUG)) {
|
||||
for (int i = 0; i < s->num_chmaps; i++) {
|
||||
struct mp_chmap c = s->chmaps[i];
|
||||
MP_DBG(ao, "chmap_sel #%d: %s (%s)\n", i, mp_chmap_to_str(&c),
|
||||
mp_chmap_to_str_hr(&c));
|
||||
}
|
||||
}
|
||||
MP_VERBOSE(ao, "Channel layouts:\n");
|
||||
mp_chmal_sel_log(s, ao->log, MSGL_V);
|
||||
bool r = mp_chmap_sel_adjust(s, map);
|
||||
if (r) {
|
||||
MP_DBG(ao, "result: %s (%s)\n", mp_chmap_to_str(map),
|
||||
mp_chmap_to_str_hr(map));
|
||||
}
|
||||
if (r)
|
||||
MP_VERBOSE(ao, "result: %s\n", mp_chmap_to_str(map));
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ struct encode_lavc_context;
|
||||
struct mp_audio;
|
||||
|
||||
struct ao *ao_init_best(struct mpv_global *global,
|
||||
bool ao_null_fallback,
|
||||
struct input_ctx *input_ctx,
|
||||
struct encode_lavc_context *encode_lavc_ctx,
|
||||
int samplerate, int format, struct mp_chmap channels);
|
||||
|
||||
+45
-25
@@ -50,6 +50,7 @@
|
||||
|
||||
struct priv {
|
||||
snd_pcm_t *alsa;
|
||||
bool device_lost;
|
||||
snd_pcm_format_t alsa_fmt;
|
||||
int can_pause;
|
||||
snd_pcm_sframes_t prepause_frames;
|
||||
@@ -83,6 +84,21 @@ struct priv {
|
||||
MP_WARN(ao, "%s: %s\n", (message), snd_strerror(err)); \
|
||||
} while (0)
|
||||
|
||||
// Common code for handling ENODEV, which happens if a device gets "lost", and
|
||||
// can't be used anymore. Returns true if alsa_err is not ENODEV.
|
||||
static bool check_device_present(struct ao *ao, int alsa_err)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
if (alsa_err != -ENODEV)
|
||||
return true;
|
||||
if (!p->device_lost) {
|
||||
MP_WARN(ao, "Device lost, trying to recover...\n");
|
||||
ao_request_reload(ao);
|
||||
p->device_lost = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
@@ -210,11 +226,20 @@ static const int mp_to_alsa_format[][2] = {
|
||||
{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_DOUBLE, SND_PCM_FORMAT_FLOAT64},
|
||||
{AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN},
|
||||
};
|
||||
|
||||
static int find_alsa_format(int af_format)
|
||||
{
|
||||
if (af_fmt_is_spdif(af_format)) {
|
||||
if (af_format == AF_FORMAT_S_MP3) {
|
||||
return SND_PCM_FORMAT_MPEG;
|
||||
} else {
|
||||
return SND_PCM_FORMAT_S16;
|
||||
}
|
||||
}
|
||||
|
||||
af_format = af_fmt_from_planar(af_format);
|
||||
for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) {
|
||||
if (mp_to_alsa_format[n][0] == af_format)
|
||||
@@ -303,9 +328,9 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
|
||||
if (mp_chmap_is_valid(&entry)) {
|
||||
if (maps[i]->type == SND_CHMAP_TYPE_VAR)
|
||||
mp_chmap_reorder_norm(&entry);
|
||||
MP_VERBOSE(ao, "Got supported channel map: %s (type %s)\n",
|
||||
mp_chmap_to_str(&entry),
|
||||
snd_pcm_chmap_type_name(maps[i]->type));
|
||||
MP_DBG(ao, "Got supported channel map: %s (type %s)\n",
|
||||
mp_chmap_to_str(&entry),
|
||||
snd_pcm_chmap_type_name(maps[i]->type));
|
||||
mp_chmap_sel_add_map(&chmap_sel, &entry);
|
||||
} else {
|
||||
char tmp[128];
|
||||
@@ -453,28 +478,22 @@ static int init_device(struct ao *ao, bool second_try)
|
||||
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
|
||||
CHECK_ALSA_ERROR("Unable to get initial parameters");
|
||||
|
||||
if (af_fmt_is_spdif(ao->format)) {
|
||||
if (ao->format == AF_FORMAT_S_MP3) {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_MPEG;
|
||||
} else {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
bool found_format = false;
|
||||
int try_formats[AF_FORMAT_COUNT];
|
||||
af_get_best_sample_formats(ao->format, try_formats);
|
||||
for (int n = 0; try_formats[n]; n++) {
|
||||
p->alsa_fmt = find_alsa_format(try_formats[n]);
|
||||
MP_VERBOSE(ao, "trying format %s\n", af_fmt_to_str(try_formats[n]));
|
||||
if (snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt) >= 0) {
|
||||
ao->format = try_formats[n];
|
||||
found_format = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||
if (err < 0) {
|
||||
if (af_fmt_is_spdif(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;
|
||||
if (!found_format) {
|
||||
MP_ERR(ao, "Can't find appropriate sample format.\n");
|
||||
goto alsa_error;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||
@@ -731,6 +750,8 @@ static int get_space(struct ao *ao)
|
||||
snd_pcm_status_alloca(&status);
|
||||
|
||||
err = snd_pcm_status(p->alsa, status);
|
||||
if (!check_device_present(ao, err))
|
||||
goto alsa_error;
|
||||
CHECK_ALSA_ERROR("cannot get pcm status");
|
||||
|
||||
unsigned space = snd_pcm_status_get_avail(status);
|
||||
@@ -857,9 +878,8 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
if (res == -EINTR || res == -EAGAIN) { /* retry */
|
||||
res = 0;
|
||||
} else if (res == -ENODEV) {
|
||||
MP_WARN(ao, "Device lost, trying to recover...\n");
|
||||
ao_request_reload(ao);
|
||||
} else if (!check_device_present(ao, res)) {
|
||||
goto alsa_error;
|
||||
} else if (res < 0) {
|
||||
if (res == -ESTRPIPE) { /* suspend */
|
||||
resume_device(ao);
|
||||
|
||||
@@ -180,6 +180,8 @@ static void init_physical_format(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
OSErr err;
|
||||
|
||||
void *tmp = talloc_new(NULL);
|
||||
|
||||
AudioStreamBasicDescription asbd;
|
||||
ca_fill_asbd(ao, &asbd);
|
||||
|
||||
@@ -190,6 +192,8 @@ static void init_physical_format(struct ao *ao)
|
||||
&streams, &n_streams);
|
||||
CHECK_CA_ERROR("could not get number of streams");
|
||||
|
||||
talloc_steal(tmp, streams);
|
||||
|
||||
MP_VERBOSE(ao, "Found %zd substream(s).\n", n_streams);
|
||||
|
||||
for (int i = 0; i < n_streams; i++) {
|
||||
@@ -204,6 +208,7 @@ static void init_physical_format(struct ao *ao)
|
||||
if (!CHECK_CA_WARN("could not get number of stream formats"))
|
||||
continue; // try next one
|
||||
|
||||
talloc_steal(tmp, formats);
|
||||
|
||||
uint32_t direction;
|
||||
err = CA_GET(streams[i], kAudioStreamPropertyDirection, &direction);
|
||||
@@ -232,6 +237,12 @@ static void init_physical_format(struct ao *ao)
|
||||
&p->original_asbd);
|
||||
CHECK_CA_WARN("could not get current physical stream format");
|
||||
|
||||
if (ca_asbd_equals(&p->original_asbd, &best_asbd)) {
|
||||
MP_VERBOSE(ao, "Requested format already set, not changing.\n");
|
||||
p->original_asbd.mFormatID = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ca_change_physical_format_sync(ao, streams[i], best_asbd))
|
||||
p->original_asbd = (AudioStreamBasicDescription){0};
|
||||
break;
|
||||
@@ -239,6 +250,7 @@ static void init_physical_format(struct ao *ao)
|
||||
}
|
||||
|
||||
coreaudio_error:
|
||||
talloc_free(tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,9 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
|
||||
AudioChannelLayout *r;
|
||||
OSStatus err;
|
||||
|
||||
if (tag == kAudioChannelLayoutTag_UseChannelDescriptions)
|
||||
return l;
|
||||
|
||||
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) {
|
||||
uint32_t psize;
|
||||
err = AudioFormatGetPropertyInfo(
|
||||
@@ -109,7 +112,7 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
|
||||
kAudioFormatProperty_ChannelLayoutForBitmap,
|
||||
sizeof(uint32_t), &l->mChannelBitmap, &psize, r);
|
||||
CHECK_CA_ERROR("failed to convert channel bitmap to descriptions (get)");
|
||||
} else if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
|
||||
} else {
|
||||
uint32_t psize;
|
||||
err = AudioFormatGetPropertyInfo(
|
||||
kAudioFormatProperty_ChannelLayoutForTag,
|
||||
@@ -120,10 +123,11 @@ static AudioChannelLayout *ca_layout_to_custom_layout(struct ao *ao,
|
||||
kAudioFormatProperty_ChannelLayoutForTag,
|
||||
sizeof(AudioChannelLayoutTag), &l->mChannelLayoutTag, &psize, r);
|
||||
CHECK_CA_ERROR("failed to convert channel tag to descriptions (get)");
|
||||
} else {
|
||||
r = l;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "converted input channel layout:\n");
|
||||
ca_log_layout(ao, MSGL_V, l);
|
||||
|
||||
return r;
|
||||
coreaudio_error:
|
||||
return NULL;
|
||||
@@ -134,22 +138,20 @@ static bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
|
||||
{
|
||||
void *talloc_ctx = talloc_new(NULL);
|
||||
|
||||
MP_DBG(ao, "input channel layout:\n");
|
||||
ca_log_layout(ao, MSGL_DEBUG, layout);
|
||||
MP_VERBOSE(ao, "input channel layout:\n");
|
||||
ca_log_layout(ao, MSGL_V, layout);
|
||||
|
||||
AudioChannelLayout *l = ca_layout_to_custom_layout(ao, talloc_ctx, layout);
|
||||
if (!l)
|
||||
goto coreaudio_error;
|
||||
|
||||
MP_VERBOSE(ao, "converted input channel layout:\n");
|
||||
ca_log_layout(ao, MSGL_V, l);
|
||||
|
||||
if (l->mNumberChannelDescriptions > MP_NUM_CHANNELS) {
|
||||
MP_VERBOSE(ao, "layout has too many descriptions (%u, max: %d)\n",
|
||||
(unsigned) l->mNumberChannelDescriptions, MP_NUM_CHANNELS);
|
||||
return false;
|
||||
}
|
||||
|
||||
chmap->num = l->mNumberChannelDescriptions;
|
||||
for (int n = 0; n < l->mNumberChannelDescriptions; n++) {
|
||||
AudioChannelLabel label = l->mChannelDescriptions[n].mChannelLabel;
|
||||
int speaker = ca_label_to_mp_speaker_id(label);
|
||||
@@ -157,14 +159,13 @@ static bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
|
||||
MP_VERBOSE(ao, "channel label=%u unusable to build channel "
|
||||
"bitmap, skipping layout\n", (unsigned) label);
|
||||
goto coreaudio_error;
|
||||
} else {
|
||||
chmap->speaker[n] = speaker;
|
||||
chmap->num = n + 1;
|
||||
}
|
||||
chmap->speaker[n] = speaker;
|
||||
}
|
||||
|
||||
talloc_free(talloc_ctx);
|
||||
return chmap->num > 0;
|
||||
MP_VERBOSE(ao, "mp chmap: %s\n", mp_chmap_to_str(chmap));
|
||||
return mp_chmap_is_valid(chmap) && !mp_chmap_is_unknown(chmap);
|
||||
coreaudio_error:
|
||||
MP_VERBOSE(ao, "converted input channel layout (failed):\n");
|
||||
ca_log_layout(ao, MSGL_V, layout);
|
||||
@@ -234,34 +235,68 @@ coreaudio_error:
|
||||
return r;
|
||||
}
|
||||
|
||||
bool ca_init_chmap(struct ao *ao, AudioDeviceID device)
|
||||
static void ca_retrieve_layouts(struct ao *ao, struct mp_chmap_sel *s,
|
||||
AudioDeviceID device)
|
||||
{
|
||||
void *ta_ctx = talloc_new(NULL);
|
||||
|
||||
struct mp_chmap_sel chmap_sel = {.tmp = ta_ctx};
|
||||
struct mp_chmap chmap = {0};
|
||||
|
||||
mp_chmap_sel_add_map(&chmap_sel, &(struct mp_chmap)MP_CHMAP_INIT_MONO);
|
||||
struct mp_chmap chmap;
|
||||
|
||||
AudioChannelLayout *ml = ca_query_layout(ao, device, ta_ctx);
|
||||
if (ml && ca_layout_to_mp_chmap(ao, ml, &chmap))
|
||||
mp_chmap_sel_add_map(&chmap_sel, &chmap);
|
||||
mp_chmap_sel_add_map(s, &chmap);
|
||||
|
||||
AudioChannelLayout *sl = ca_query_stereo_layout(ao, device, ta_ctx);
|
||||
if (sl && ca_layout_to_mp_chmap(ao, sl, &chmap))
|
||||
mp_chmap_sel_add_map(&chmap_sel, &chmap);
|
||||
mp_chmap_sel_add_map(s, &chmap);
|
||||
|
||||
talloc_free(ta_ctx);
|
||||
}
|
||||
|
||||
bool ca_init_chmap(struct ao *ao, AudioDeviceID device)
|
||||
{
|
||||
struct mp_chmap_sel chmap_sel = {0};
|
||||
ca_retrieve_layouts(ao, &chmap_sel, device);
|
||||
|
||||
if (!chmap_sel.num_chmaps)
|
||||
mp_chmap_sel_add_map(&chmap_sel, &(struct mp_chmap)MP_CHMAP_INIT_STEREO);
|
||||
|
||||
mp_chmap_sel_add_map(&chmap_sel, &(struct mp_chmap)MP_CHMAP_INIT_MONO);
|
||||
|
||||
if (!ao_chmap_sel_adjust(ao, &chmap_sel, &ao->channels)) {
|
||||
MP_ERR(ao, "could not select a suitable channel map among the "
|
||||
"hardware supported ones. Make sure to configure your "
|
||||
"output device correctly in 'Audio MIDI Setup.app'\n");
|
||||
goto coreaudio_error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
|
||||
struct mp_chmap *out_map)
|
||||
{
|
||||
// Apparently, we have to guess by looking back at the supported layouts,
|
||||
// and I haven't found a property that retrieves the actual currently
|
||||
// active channel layout.
|
||||
|
||||
struct mp_chmap_sel chmap_sel = {0};
|
||||
ca_retrieve_layouts(ao, &chmap_sel, device);
|
||||
|
||||
// Use any exact match.
|
||||
for (int n = 0; n < chmap_sel.num_chmaps; n++) {
|
||||
if (chmap_sel.chmaps[n].num == channel_count) {
|
||||
MP_VERBOSE(ao, "mismatching channels - fallback #%d\n", n);
|
||||
*out_map = chmap_sel.chmaps[n];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(ta_ctx);
|
||||
return true;
|
||||
|
||||
coreaudio_error:
|
||||
talloc_free(ta_ctx);
|
||||
return false;
|
||||
// Fall back to stereo or mono, and fill the rest with silence. (We don't
|
||||
// know what the device expects. We could use a larger default layout here,
|
||||
// but let's not.)
|
||||
mp_chmap_from_channels(out_map, MPMIN(2, channel_count));
|
||||
out_map->num = channel_count;
|
||||
for (int n = 2; n < out_map->num; n++)
|
||||
out_map->speaker[n] = MP_SPEAKER_ID_NA;
|
||||
MP_WARN(ao, "mismatching channels - falling back to %s\n",
|
||||
mp_chmap_to_str(out_map));
|
||||
}
|
||||
@@ -20,6 +20,10 @@
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
struct mp_chmap;
|
||||
|
||||
bool ca_init_chmap(struct ao *ao, AudioDeviceID device);
|
||||
void ca_get_active_chmap(struct ao *ao, AudioDeviceID device, int channel_count,
|
||||
struct mp_chmap *out_map);
|
||||
|
||||
#endif
|
||||
@@ -300,6 +300,12 @@ static int init(struct ao *ao)
|
||||
|
||||
ca_print_asbd(ao, "virtual format", &p->stream_asbd);
|
||||
|
||||
if (p->stream_asbd.mChannelsPerFrame > MP_NUM_CHANNELS) {
|
||||
MP_ERR(ao, "unsupported number of channels: %d > %d.\n",
|
||||
p->stream_asbd.mChannelsPerFrame, MP_NUM_CHANNELS);
|
||||
goto coreaudio_error;
|
||||
}
|
||||
|
||||
int new_format = ca_asbd_to_mp_format(&p->stream_asbd);
|
||||
|
||||
// If both old and new formats are spdif, avoid changing it due to the
|
||||
@@ -315,7 +321,10 @@ static int init(struct ao *ao)
|
||||
ao->samplerate = p->stream_asbd.mSampleRate;
|
||||
|
||||
if (ao->channels.num != p->stream_asbd.mChannelsPerFrame) {
|
||||
// We really expect that ca_init_chmap() fixes the layout to the HW's.
|
||||
ca_get_active_chmap(ao, p->device, p->stream_asbd.mChannelsPerFrame,
|
||||
&ao->channels);
|
||||
}
|
||||
if (!ao->channels.num) {
|
||||
MP_ERR(ao, "number of channels changed, and unknown channel layout!\n");
|
||||
goto coreaudio_error;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,9 @@ static bool ca_is_output_device(struct ao *ao, AudioDeviceID dev)
|
||||
size_t n_buffers;
|
||||
AudioBufferList *buffers;
|
||||
const ca_scope scope = kAudioDevicePropertyStreamConfiguration;
|
||||
CA_GET_ARY_O(dev, scope, &buffers, &n_buffers);
|
||||
OSStatus err = CA_GET_ARY_O(dev, scope, &buffers, &n_buffers);
|
||||
if (err != noErr)
|
||||
return false;
|
||||
talloc_free(buffers);
|
||||
return n_buffers > 0;
|
||||
}
|
||||
@@ -71,11 +73,16 @@ void ca_get_device_list(struct ao *ao, struct ao_device_list *list)
|
||||
char *name;
|
||||
char *desc;
|
||||
err = CA_GET_STR(devs[i], kAudioDevicePropertyDeviceUID, &name);
|
||||
if (err != noErr) {
|
||||
MP_VERBOSE(ao, "skipping device %d, which has no UID\n", i);
|
||||
talloc_free(ta_ctx);
|
||||
continue;
|
||||
}
|
||||
talloc_steal(ta_ctx, name);
|
||||
err = CA_GET_STR(devs[i], kAudioObjectPropertyName, &desc);
|
||||
talloc_steal(ta_ctx, desc);
|
||||
if (err != noErr)
|
||||
desc = "Unknown";
|
||||
desc = talloc_strdup(NULL, "Unknown");
|
||||
talloc_steal(ta_ctx, desc);
|
||||
ao_device_list_add(list, ao, &(struct ao_device_desc){name, desc});
|
||||
talloc_free(ta_ctx);
|
||||
}
|
||||
@@ -498,6 +505,8 @@ bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
|
||||
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &prev_format);
|
||||
CHECK_CA_ERROR("can't get current physical format");
|
||||
|
||||
ca_print_asbd(ao, "format in use before switching:", &prev_format);
|
||||
|
||||
/* Install the callback. */
|
||||
AudioObjectPropertyAddress p_addr = {
|
||||
.mSelector = kAudioStreamPropertyPhysicalFormat,
|
||||
@@ -516,7 +525,7 @@ bool ca_change_physical_format_sync(struct ao *ao, AudioStreamID stream,
|
||||
|
||||
/* The AudioStreamSetProperty is not only asynchronous,
|
||||
* it is also not Atomic, in its behaviour. */
|
||||
struct timespec timeout = mp_rel_time_to_timespec(0.5);
|
||||
struct timespec timeout = mp_rel_time_to_timespec(2.0);
|
||||
AudioStreamBasicDescription actual_format = {0};
|
||||
while (1) {
|
||||
err = CA_GET(stream, kAudioStreamPropertyPhysicalFormat, &actual_format);
|
||||
|
||||
+18
-20
@@ -58,33 +58,28 @@ struct priv {
|
||||
bool shutdown;
|
||||
};
|
||||
|
||||
static void select_format(struct ao *ao, AVCodec *codec)
|
||||
static bool supports_format(AVCodec *codec, int format)
|
||||
{
|
||||
int best_score = INT_MIN;
|
||||
int best_format = 0;
|
||||
|
||||
// Check the encoder's list of supported formats.
|
||||
for (const enum AVSampleFormat *sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat)
|
||||
{
|
||||
int fmt = af_from_avformat(*sampleformat);
|
||||
if (!fmt) {
|
||||
MP_WARN(ao, "unsupported lavc format %s\n",
|
||||
av_get_sample_fmt_name(*sampleformat));
|
||||
continue;
|
||||
}
|
||||
int score = af_format_conversion_score(fmt, ao->format);
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
best_format = fmt;
|
||||
}
|
||||
if (af_from_avformat(*sampleformat) == format)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (best_format) {
|
||||
ao->format = best_format;
|
||||
} else {
|
||||
MP_ERR(ao, "sample format not found\n"); // shouldn't happen
|
||||
static void select_format(struct ao *ao, AVCodec *codec)
|
||||
{
|
||||
int formats[AF_FORMAT_COUNT];
|
||||
af_get_best_sample_formats(ao->format, formats);
|
||||
|
||||
for (int n = 0; formats[n]; n++) {
|
||||
if (supports_format(codec, formats[n])) {
|
||||
ao->format = formats[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +164,9 @@ static int init(struct ao *ao)
|
||||
|
||||
ao->untimed = true;
|
||||
|
||||
if (ao->channels.num > AV_NUM_DATA_POINTERS)
|
||||
goto fail;
|
||||
|
||||
pthread_mutex_unlock(&ao->encode_lavc_ctx->lock);
|
||||
return 0;
|
||||
|
||||
|
||||
+2
-1
@@ -313,7 +313,8 @@ static int reopen_device(struct ao *ao, bool allow_format_changes)
|
||||
}
|
||||
}
|
||||
|
||||
int try_formats[] = {format, AF_FORMAT_S32, AF_FORMAT_S24, AF_FORMAT_S16, 0};
|
||||
int try_formats[AF_FORMAT_COUNT];
|
||||
af_get_best_sample_formats(format, try_formats);
|
||||
for (int n = 0; try_formats[n]; n++) {
|
||||
format = try_formats[n];
|
||||
if (try_format(ao, &format))
|
||||
|
||||
@@ -486,10 +486,8 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
|
||||
}
|
||||
} else {
|
||||
change_waveformat_channels(wformat, &entry);
|
||||
if (try_format_exclusive(ao, wformat)) {
|
||||
if (try_format_exclusive(ao, wformat))
|
||||
mp_chmap_sel_add_map(&chmap_sel, &entry);
|
||||
MP_VERBOSE(ao, "%s is supported\n", mp_chmap_to_str(&entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
|
||||
avctx->block_align = st->block_align;
|
||||
avctx->channel_layout = st->channel_layout;
|
||||
avctx->bits_per_coded_sample = st->bits_per_coded_sample;
|
||||
avctx->has_b_frames = st->has_b_frames;
|
||||
}
|
||||
|
||||
// We merely pass-through our PTS/DTS as an int64_t; libavcodec won't use it.
|
||||
|
||||
+2
-4
@@ -326,10 +326,8 @@ static void write_msg_to_buffers(struct mp_log *log, int lev, char *text)
|
||||
static void dump_stats(struct mp_log *log, int lev, char *text)
|
||||
{
|
||||
struct mp_log_root *root = log->root;
|
||||
if (lev == MSGL_STATS && root->stats_file) {
|
||||
fprintf(root->stats_file, "%"PRId64" %s #%s\n", mp_time_us(), text,
|
||||
log->verbose_prefix);
|
||||
}
|
||||
if (lev == MSGL_STATS && root->stats_file)
|
||||
fprintf(root->stats_file, "%"PRId64" %s\n", mp_time_us(), text);
|
||||
}
|
||||
|
||||
void mp_msg_va(struct mp_log *log, int lev, const char *format, va_list va)
|
||||
|
||||
+23
-9
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "misc/bstr.h"
|
||||
#include "common/common.h"
|
||||
#include "common/tags.h"
|
||||
|
||||
#include "cue.h"
|
||||
|
||||
@@ -37,6 +38,7 @@ enum cue_command {
|
||||
CUE_TRACK,
|
||||
CUE_INDEX,
|
||||
CUE_TITLE,
|
||||
CUE_PERFORMER,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
@@ -51,7 +53,7 @@ static const struct {
|
||||
{ CUE_UNUSED, "CDTEXTFILE" },
|
||||
{ CUE_UNUSED, "FLAGS" },
|
||||
{ CUE_UNUSED, "ISRC" },
|
||||
{ CUE_UNUSED, "PERFORMER" },
|
||||
{ CUE_PERFORMER, "PERFORMER" },
|
||||
{ CUE_UNUSED, "POSTGAP" },
|
||||
{ CUE_UNUSED, "PREGAP" },
|
||||
{ CUE_UNUSED, "REM" },
|
||||
@@ -160,17 +162,19 @@ bool mp_probe_cue(struct bstr data)
|
||||
struct cue_file *mp_parse_cue(struct bstr data)
|
||||
{
|
||||
struct cue_file *f = talloc_zero(NULL, struct cue_file);
|
||||
f->tags = talloc_zero(f, struct mp_tags);
|
||||
|
||||
data = skip_utf8_bom(data);
|
||||
|
||||
char *filename = NULL;
|
||||
// Global metadata, and copied into new tracks.
|
||||
struct cue_track proto_track = {0};
|
||||
struct cue_track *cur_track = &proto_track;
|
||||
struct cue_track *cur_track = NULL;
|
||||
|
||||
while (data.len) {
|
||||
struct bstr param;
|
||||
switch (read_cmd(&data, ¶m)) {
|
||||
int cmd = read_cmd(&data, ¶m);
|
||||
switch (cmd) {
|
||||
case CUE_ERROR:
|
||||
talloc_free(f);
|
||||
return NULL;
|
||||
@@ -179,19 +183,29 @@ struct cue_file *mp_parse_cue(struct bstr data)
|
||||
f->num_tracks += 1;
|
||||
cur_track = &f->tracks[f->num_tracks - 1];
|
||||
*cur_track = proto_track;
|
||||
cur_track->tags = talloc_zero(f, struct mp_tags);
|
||||
break;
|
||||
}
|
||||
case CUE_TITLE:
|
||||
cur_track->title = read_quoted(f, ¶m);
|
||||
case CUE_PERFORMER: {
|
||||
static const char *metanames[] = {
|
||||
[CUE_TITLE] = "title",
|
||||
[CUE_PERFORMER] = "performer",
|
||||
};
|
||||
struct mp_tags *tags = cur_track ? cur_track->tags : f->tags;
|
||||
mp_tags_set_bstr(tags, bstr0(metanames[cmd]), param);
|
||||
break;
|
||||
}
|
||||
case CUE_INDEX: {
|
||||
int type = read_int_2(¶m);
|
||||
double time = read_time(¶m);
|
||||
if (type == 1) {
|
||||
cur_track->start = time;
|
||||
cur_track->filename = filename;
|
||||
} else if (type == 0) {
|
||||
cur_track->pregap_start = time;
|
||||
if (cur_track) {
|
||||
if (type == 1) {
|
||||
cur_track->start = time;
|
||||
cur_track->filename = filename;
|
||||
} else if (type == 0) {
|
||||
cur_track->pregap_start = time;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@
|
||||
struct cue_file {
|
||||
struct cue_track *tracks;
|
||||
int num_tracks;
|
||||
struct mp_tags *tags;
|
||||
};
|
||||
|
||||
struct cue_track {
|
||||
@@ -32,7 +33,7 @@ struct cue_track {
|
||||
double start; // corresponds to INDEX 01
|
||||
char *filename;
|
||||
int source;
|
||||
char *title;
|
||||
struct mp_tags *tags;
|
||||
};
|
||||
|
||||
bool mp_probe_cue(struct bstr data);
|
||||
|
||||
+13
-12
@@ -307,13 +307,12 @@ const char *stream_type_name(enum stream_type type)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the same value as demuxer->fill_buffer: 1 ok, 0 EOF/not selected.
|
||||
int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
{
|
||||
struct demux_stream *ds = stream ? stream->ds : NULL;
|
||||
if (!dp || !ds) {
|
||||
talloc_free(dp);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
struct demux_internal *in = ds->in;
|
||||
pthread_mutex_lock(&in->lock);
|
||||
@@ -330,7 +329,7 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
if (!ds->selected || in->seeking || drop) {
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
talloc_free(dp);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dp->stream = stream->index;
|
||||
@@ -371,7 +370,6 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
ds->in->wakeup_cb(ds->in->wakeup_cb_ctx);
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns true if there was "progress" (lock was released temporarily).
|
||||
@@ -430,10 +428,8 @@ static bool read_packet(struct demux_internal *in)
|
||||
pthread_mutex_lock(&in->lock);
|
||||
|
||||
if (eof) {
|
||||
for (int n = 0; n < in->d_buffer->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_buffer->streams[n]->ds;
|
||||
ds->eof = true;
|
||||
}
|
||||
for (int n = 0; n < in->d_buffer->num_streams; n++)
|
||||
in->d_buffer->streams[n]->ds->eof = true;
|
||||
// If we had EOF previously, then don't wakeup (avoids wakeup loop)
|
||||
if (!in->last_eof) {
|
||||
if (in->wakeup_cb)
|
||||
@@ -924,7 +920,8 @@ static void demux_init_cuesheet(struct demuxer *demuxer)
|
||||
if (f) {
|
||||
for (int n = 0; n < f->num_tracks; n++) {
|
||||
struct cue_track *t = &f->tracks[n];
|
||||
demuxer_add_chapter(demuxer, t->title, t->start, -1);
|
||||
int idx = demuxer_add_chapter(demuxer, "", t->start, -1);
|
||||
mp_tags_merge(demuxer->chapters[idx].metadata, t->tags);
|
||||
}
|
||||
}
|
||||
talloc_free(f);
|
||||
@@ -938,6 +935,9 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
struct demuxer_params *params,
|
||||
enum demux_check check)
|
||||
{
|
||||
if (mp_cancel_test(stream->cancel))
|
||||
return NULL;
|
||||
|
||||
struct demuxer *demuxer = talloc_ptrtype(NULL, demuxer);
|
||||
*demuxer = (struct demuxer) {
|
||||
.desc = desc,
|
||||
@@ -1392,10 +1392,11 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
|
||||
case DEMUXER_CTRL_GET_BITRATE_STATS: {
|
||||
double *rates = arg;
|
||||
for (int n = 0; n < STREAM_TYPE_COUNT; n++)
|
||||
rates[n] = 0;
|
||||
rates[n] = -1;
|
||||
for (int n = 0; n < in->d_user->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_user->streams[n]->ds;
|
||||
rates[ds->type] += MPMAX(0, ds->bitrate);
|
||||
if (ds->selected && ds->bitrate >= 0)
|
||||
rates[ds->type] = MPMAX(0, rates[ds->type]) + ds->bitrate;
|
||||
}
|
||||
return DEMUXER_CTRL_OK;
|
||||
}
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@ typedef struct {
|
||||
void free_demuxer(struct demuxer *demuxer);
|
||||
void free_demuxer_and_stream(struct demuxer *demuxer);
|
||||
|
||||
int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp);
|
||||
void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp);
|
||||
|
||||
struct demux_packet *demux_read_packet(struct sh_stream *sh);
|
||||
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt);
|
||||
|
||||
+15
-15
@@ -38,7 +38,7 @@
|
||||
#define PROBE_SIZE 512
|
||||
|
||||
struct priv {
|
||||
bstr data;
|
||||
struct cue_file *f;
|
||||
};
|
||||
|
||||
static void add_source(struct timeline *tl, struct demuxer *d)
|
||||
@@ -150,15 +150,8 @@ static void build_timeline(struct timeline *tl)
|
||||
|
||||
add_source(tl, tl->demuxer);
|
||||
|
||||
struct cue_file *f = mp_parse_cue(p->data);
|
||||
if (!f) {
|
||||
MP_ERR(tl, "CUE: error parsing input file!\n");
|
||||
goto out;
|
||||
}
|
||||
talloc_steal(ctx, f);
|
||||
|
||||
struct cue_track *tracks = f->tracks;
|
||||
size_t track_count = f->num_tracks;
|
||||
struct cue_track *tracks = p->f->tracks;
|
||||
size_t track_count = p->f->num_tracks;
|
||||
|
||||
if (track_count == 0) {
|
||||
MP_ERR(tl, "CUE: no tracks found!\n");
|
||||
@@ -224,10 +217,8 @@ static void build_timeline(struct timeline *tl)
|
||||
};
|
||||
chapters[i] = (struct demux_chapter) {
|
||||
.pts = timeline[i].start,
|
||||
.metadata = talloc_zero(tl, struct mp_tags),
|
||||
.metadata = mp_tags_dup(tl, tracks[i].tags),
|
||||
};
|
||||
// might want to include other metadata here
|
||||
mp_tags_set_str(chapters[i].metadata, "title", tracks[i].title);
|
||||
starttime += duration;
|
||||
}
|
||||
|
||||
@@ -261,9 +252,18 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
|
||||
struct priv *p = talloc_zero(demuxer, struct priv);
|
||||
demuxer->priv = p;
|
||||
demuxer->fully_read = true;
|
||||
p->data = stream_read_complete(s, demuxer, 1000000);
|
||||
if (p->data.start == NULL)
|
||||
|
||||
bstr data = stream_read_complete(s, p, 1000000);
|
||||
if (data.start == NULL)
|
||||
return -1;
|
||||
p->f = mp_parse_cue(data);
|
||||
talloc_steal(p, p->f);
|
||||
if (!p->f) {
|
||||
MP_ERR(demuxer, "error parsing input file!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mp_tags_merge(demuxer->metadata, p->f->tags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+14
-5
@@ -635,7 +635,10 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
sh->ff_index = st->index;
|
||||
sh->codec = mp_codec_from_av_codec_id(codec->codec_id);
|
||||
sh->codec_tag = codec->codec_tag;
|
||||
sh->lav_headers = codec;
|
||||
sh->lav_headers = avcodec_alloc_context3(NULL);
|
||||
if (!sh->lav_headers)
|
||||
return;
|
||||
mp_copy_lav_codec_headers(sh->lav_headers, codec);
|
||||
|
||||
if (st->disposition & AV_DISPOSITION_DEFAULT)
|
||||
sh->default_track = true;
|
||||
@@ -850,7 +853,7 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
AVPacket *pkt = &(AVPacket){0};
|
||||
int r = av_read_frame(priv->avfc, pkt);
|
||||
if (r < 0) {
|
||||
av_free_packet(pkt);
|
||||
av_packet_unref(pkt);
|
||||
if (r == AVERROR(EAGAIN))
|
||||
return 1;
|
||||
if (r == AVERROR_EOF)
|
||||
@@ -867,13 +870,13 @@ 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);
|
||||
av_packet_unref(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);
|
||||
av_packet_unref(pkt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -882,8 +885,10 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
if (pkt->dts != AV_NOPTS_VALUE)
|
||||
dp->dts = pkt->dts * av_q2d(st->time_base);
|
||||
dp->duration = pkt->duration * av_q2d(st->time_base);
|
||||
#if !HAVE_AV_AVPACKET_INT64_DURATION
|
||||
if (pkt->convergence_duration > 0)
|
||||
dp->duration = pkt->convergence_duration * av_q2d(st->time_base);
|
||||
#endif
|
||||
dp->pos = pkt->pos;
|
||||
dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
|
||||
if (dp->pts != MP_NOPTS_VALUE) {
|
||||
@@ -891,7 +896,7 @@ 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);
|
||||
av_packet_unref(pkt);
|
||||
|
||||
if (priv->format_hack.clear_filepos)
|
||||
dp->pos = -1;
|
||||
@@ -1074,6 +1079,10 @@ static void demux_close_lavf(demuxer_t *demuxer)
|
||||
if (priv->pb)
|
||||
av_freep(&priv->pb->buffer);
|
||||
av_freep(&priv->pb);
|
||||
for (int n = 0; n < priv->num_streams; n++) {
|
||||
if (priv->streams[n])
|
||||
avcodec_free_context(&priv->streams[n]->lav_headers);
|
||||
}
|
||||
talloc_free(priv);
|
||||
demuxer->priv = NULL;
|
||||
}
|
||||
|
||||
+57
-19
@@ -149,6 +149,16 @@ typedef struct mkv_index {
|
||||
uint64_t filepos; // position of the cluster which contains the packet
|
||||
} mkv_index_t;
|
||||
|
||||
struct block_info {
|
||||
uint64_t duration, discardpadding;
|
||||
bool simple, keyframe;
|
||||
uint64_t timecode;
|
||||
mkv_track_t *track;
|
||||
bstr data;
|
||||
void *alloc;
|
||||
int64_t filepos;
|
||||
};
|
||||
|
||||
typedef struct mkv_demuxer {
|
||||
int64_t segment_start, segment_end;
|
||||
|
||||
@@ -183,6 +193,8 @@ typedef struct mkv_demuxer {
|
||||
bool index_has_durations;
|
||||
|
||||
bool eof_warning;
|
||||
|
||||
struct block_info tmp_block;
|
||||
} mkv_demuxer_t;
|
||||
|
||||
#define OPT_BASE_STRUCT struct demux_mkv_opts
|
||||
@@ -190,6 +202,7 @@ struct demux_mkv_opts {
|
||||
int subtitle_preroll;
|
||||
double subtitle_preroll_secs;
|
||||
int probe_duration;
|
||||
int probe_start_time;
|
||||
int fix_timestamps;
|
||||
};
|
||||
|
||||
@@ -200,12 +213,14 @@ const struct m_sub_options demux_mkv_conf = {
|
||||
M_OPT_MIN, .min = 0),
|
||||
OPT_CHOICE("probe-video-duration", probe_duration, 0,
|
||||
({"no", 0}, {"yes", 1}, {"full", 2})),
|
||||
OPT_FLAG("probe-start-time", probe_start_time, 0),
|
||||
OPT_FLAG("fix-timestamps", fix_timestamps, 0),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct demux_mkv_opts),
|
||||
.defaults = &(const struct demux_mkv_opts){
|
||||
.subtitle_preroll_secs = 1.0,
|
||||
.probe_start_time = 1,
|
||||
.fix_timestamps = 0,
|
||||
},
|
||||
};
|
||||
@@ -219,7 +234,9 @@ const struct m_sub_options demux_mkv_conf = {
|
||||
// (Subtitle packets added before first A/V keyframe packet is found with seek.)
|
||||
#define NUM_SUB_PREROLL_PACKETS 500
|
||||
|
||||
static void probe_last_timestamp(struct demuxer *demuxer);
|
||||
static void probe_last_timestamp(struct demuxer *demuxer, int64_t start_pos);
|
||||
static void probe_first_timestamp(struct demuxer *demuxer);
|
||||
static void free_block(struct block_info *block);
|
||||
|
||||
#define AAC_SYNC_EXTENSION_TYPE 0x02b7
|
||||
static int aac_get_sample_rate_index(uint32_t sample_rate)
|
||||
@@ -1884,8 +1901,9 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
|
||||
add_coverart(demuxer);
|
||||
demuxer->allow_refresh_seeks = true;
|
||||
|
||||
probe_first_timestamp(demuxer);
|
||||
if (opts->demux_mkv->probe_duration)
|
||||
probe_last_timestamp(demuxer);
|
||||
probe_last_timestamp(demuxer, start_pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2095,6 +2113,8 @@ static void mkv_seek_reset(demuxer_t *demuxer)
|
||||
}
|
||||
track->av_parser_codec = NULL;
|
||||
}
|
||||
|
||||
free_block(&mkv_d->tmp_block);
|
||||
}
|
||||
|
||||
// Copied from libavformat/matroskadec.c (FFmpeg 310f9dd / 2013-05-30)
|
||||
@@ -2268,16 +2288,6 @@ static void mkv_parse_and_add_packet(demuxer_t *demuxer, mkv_track_t *track,
|
||||
}
|
||||
}
|
||||
|
||||
struct block_info {
|
||||
uint64_t duration, discardpadding;
|
||||
bool simple, keyframe;
|
||||
uint64_t timecode;
|
||||
mkv_track_t *track;
|
||||
bstr data;
|
||||
void *alloc;
|
||||
int64_t filepos;
|
||||
};
|
||||
|
||||
static void free_block(struct block_info *block)
|
||||
{
|
||||
free(block->alloc);
|
||||
@@ -2389,8 +2399,12 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
|
||||
if (mkv_d->a_skip_to_keyframe)
|
||||
use_this_block &= keyframe;
|
||||
} else if (track->type == MATROSKA_TRACK_SUBTITLE) {
|
||||
if (!use_this_block && mkv_d->subtitle_preroll)
|
||||
use_this_block = 1;
|
||||
if (!use_this_block && mkv_d->subtitle_preroll) {
|
||||
int64_t end_time = block_info->timecode + block_info->duration;
|
||||
if (!block_info->duration)
|
||||
end_time = INT64_MAX;
|
||||
use_this_block = end_time > mkv_d->skip_to_timecode;
|
||||
}
|
||||
if (use_this_block) {
|
||||
if (mkv_d->subtitle_preroll) {
|
||||
mkv_d->subtitle_preroll--;
|
||||
@@ -2522,6 +2536,12 @@ static int read_next_block(demuxer_t *demuxer, struct block_info *block)
|
||||
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
|
||||
stream_t *s = demuxer->stream;
|
||||
|
||||
if (mkv_d->tmp_block.alloc) {
|
||||
*block = mkv_d->tmp_block;
|
||||
mkv_d->tmp_block = (struct block_info){0};
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (stream_tell(s) < mkv_d->cluster_end) {
|
||||
int64_t start_filepos = stream_tell(s);
|
||||
@@ -2847,10 +2867,9 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
|
||||
demux_mkv_fill_buffer(demuxer);
|
||||
}
|
||||
|
||||
static void probe_last_timestamp(struct demuxer *demuxer)
|
||||
static void probe_last_timestamp(struct demuxer *demuxer, int64_t start_pos)
|
||||
{
|
||||
mkv_demuxer_t *mkv_d = demuxer->priv;
|
||||
int64_t old_pos = stream_tell(demuxer->stream);
|
||||
|
||||
if (!demuxer->seekable)
|
||||
return;
|
||||
@@ -2888,10 +2907,12 @@ static void probe_last_timestamp(struct demuxer *demuxer)
|
||||
int64_t size = stream_get_size(demuxer->stream);
|
||||
stream_seek(demuxer->stream, MPMAX(size - 10 * 1024 * 1024, 0));
|
||||
if (ebml_resync_cluster(mp_null_log, demuxer->stream) < 0)
|
||||
stream_seek(demuxer->stream, old_pos); // full scan otherwise
|
||||
stream_seek(demuxer->stream, start_pos); // full scan otherwise
|
||||
}
|
||||
}
|
||||
|
||||
free_block(&mkv_d->tmp_block);
|
||||
|
||||
int64_t last_ts[STREAM_TYPE_COUNT] = {0};
|
||||
while (1) {
|
||||
struct block_info block;
|
||||
@@ -2913,12 +2934,29 @@ static void probe_last_timestamp(struct demuxer *demuxer)
|
||||
last_ts[STREAM_VIDEO] = mkv_d->cluster_tc;
|
||||
|
||||
if (last_ts[STREAM_VIDEO])
|
||||
mkv_d->duration = last_ts[STREAM_VIDEO] / 1e9;
|
||||
mkv_d->duration = last_ts[STREAM_VIDEO] / 1e9 - demuxer->start_time;
|
||||
|
||||
stream_seek(demuxer->stream, old_pos);
|
||||
stream_seek(demuxer->stream, start_pos);
|
||||
mkv_d->cluster_start = mkv_d->cluster_end = 0;
|
||||
}
|
||||
|
||||
static void probe_first_timestamp(struct demuxer *demuxer)
|
||||
{
|
||||
mkv_demuxer_t *mkv_d = demuxer->priv;
|
||||
|
||||
if (!demuxer->opts->demux_mkv->probe_start_time)
|
||||
return;
|
||||
|
||||
struct block_info block;
|
||||
if (read_next_block(demuxer, &block) > 0)
|
||||
mkv_d->tmp_block = block;
|
||||
|
||||
demuxer->start_time = mkv_d->cluster_tc / 1e9;
|
||||
|
||||
if (demuxer->start_time > 0)
|
||||
MP_VERBOSE(demuxer, "Start PTS: %f\n", demuxer->start_time);
|
||||
}
|
||||
|
||||
static int demux_mkv_control(demuxer_t *demuxer, int cmd, void *arg)
|
||||
{
|
||||
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
|
||||
|
||||
@@ -758,6 +758,10 @@ static int d_open_file(struct demuxer *demuxer, enum demux_check check)
|
||||
{
|
||||
if (check > DEMUX_CHECK_REQUEST)
|
||||
return -1;
|
||||
#if LIBAVUTIL_VERSION_MICRO >= 100
|
||||
if (check != DEMUX_CHECK_FORCE)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (!demuxer->params || !demuxer->params->expect_subtitle)
|
||||
return -1;
|
||||
@@ -800,7 +804,9 @@ static int d_fill_buffer(struct demuxer *demuxer)
|
||||
struct priv *p = demuxer->priv;
|
||||
struct demux_packet *dp = demux_packet_list_fill(p->pkts, p->num_pkts,
|
||||
&p->current);
|
||||
return demux_add_packet(p->sh, dp);
|
||||
int have_packet = !!dp;
|
||||
demux_add_packet(p->sh, dp);
|
||||
return have_packet;
|
||||
}
|
||||
|
||||
static void d_seek(struct demuxer *demuxer, double secs, int flags)
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ const struct mp_cmd_def mp_cmds[] = {
|
||||
{ MP_CMD_RUN, "run", { ARG_STRING, ARG_STRING }, .vararg = true },
|
||||
|
||||
{ MP_CMD_SET, "set", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_ADD, "add", { ARG_STRING, OARG_DOUBLE(0) },
|
||||
{ MP_CMD_ADD, "add", { ARG_STRING, OARG_DOUBLE(1) },
|
||||
.allow_auto_repeat = true},
|
||||
{ MP_CMD_CYCLE, "cycle", {
|
||||
ARG_STRING,
|
||||
|
||||
@@ -471,8 +471,15 @@ static int parse_cycle_dir(struct mp_log *log, const struct m_option *opt,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void copy_opt(const m_option_t *opt, void *dst, const void *src)
|
||||
{
|
||||
if (dst && src)
|
||||
memcpy(dst, src, opt->type->size);
|
||||
}
|
||||
|
||||
const struct m_option_type m_option_type_cycle_dir = {
|
||||
.name = "up|down",
|
||||
.parse = parse_cycle_dir,
|
||||
.copy = copy_opt,
|
||||
.size = sizeof(double),
|
||||
};
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
#include "event.h"
|
||||
#include "input.h"
|
||||
#include "common/msg.h"
|
||||
#include "sub/find_subfiles.h"
|
||||
#include "player/external_files.h"
|
||||
|
||||
void mp_event_drop_files(struct input_ctx *ictx, int num_files, char **files,
|
||||
enum mp_dnd_action action)
|
||||
|
||||
+1
-1
@@ -1229,7 +1229,7 @@ void mp_input_load(struct input_ctx *ictx)
|
||||
bool config_ok = false;
|
||||
if (input_conf->config_file)
|
||||
config_ok = parse_config_file(ictx, input_conf->config_file, true);
|
||||
if (!config_ok && ictx->global->opts->load_config) {
|
||||
if (!config_ok) {
|
||||
// Try global conf dir
|
||||
void *tmp = talloc_new(NULL);
|
||||
char **files = mp_find_all_config_files(tmp, ictx->global, "input.conf");
|
||||
|
||||
@@ -36,6 +36,7 @@ struct key_name {
|
||||
static const struct key_name key_names[] = {
|
||||
{ ' ', "SPACE" },
|
||||
{ '#', "SHARP" },
|
||||
{ 0x3000, "IDEOGRAPHIC_SPACE" },
|
||||
{ MP_KEY_ENTER, "ENTER" },
|
||||
{ MP_KEY_TAB, "TAB" },
|
||||
{ MP_KEY_BACKSPACE, "BS" },
|
||||
|
||||
+20
-4
@@ -49,13 +49,29 @@ extern "C" {
|
||||
* 2) Using mpv as a library with mpv_create(). This basically allows embedding
|
||||
* mpv in other applications.
|
||||
*
|
||||
* Documentation
|
||||
* -------------
|
||||
*
|
||||
* The libmpv C API is documented directly in this header. Note that most
|
||||
* actual interaction with this player is done through
|
||||
* options/commands/properties, which can be accessed through this API.
|
||||
* Essentially everything is done with them, including loading a file,
|
||||
* retrieving playback progress, and so on.
|
||||
*
|
||||
* These are documented elsewhere:
|
||||
* * http://mpv.io/manual/master/#options
|
||||
* * http://mpv.io/manual/master/#list-of-input-commands
|
||||
* * http://mpv.io/manual/master/#properties
|
||||
*
|
||||
* You can also look at the examples in DOCS/client_api_examples/.
|
||||
*
|
||||
* Event loop
|
||||
* ----------
|
||||
*
|
||||
* In general, the API user should run an event loop in order to receive events.
|
||||
* This even loop should call mpv_wait_event(), which will return once a new
|
||||
* mpv client API is available. It should also be possible to integrate client
|
||||
* API usage in other event loops (e.g. GUI toolkits) with the
|
||||
* This event loop should call mpv_wait_event(), which will return once a new
|
||||
* mpv client API is available. It is also possible to integrate client API
|
||||
* usage in other event loops (e.g. GUI toolkits) with the
|
||||
* mpv_set_wakeup_callback() function, and then polling for events by calling
|
||||
* mpv_wait_event() with a 0 timeout.
|
||||
*
|
||||
@@ -198,7 +214,7 @@ extern "C" {
|
||||
* relational operators (<, >, <=, >=).
|
||||
*/
|
||||
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
|
||||
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 19)
|
||||
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 20)
|
||||
|
||||
/**
|
||||
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
|
||||
|
||||
+38
-10
@@ -108,8 +108,36 @@ extern "C" {
|
||||
* context must have been uninitialized. If this doesn't happen, undefined
|
||||
* behavior will result.
|
||||
*
|
||||
* Special D3D interop considerations
|
||||
* ----------------------------------
|
||||
* Special windowing system interop considerations
|
||||
* ------------------------------------------------
|
||||
*
|
||||
* In some cases, libmpv needs to have access to the windowing system's handles.
|
||||
* This can be a pointer to a X11 "Display" for example. Usually this is needed
|
||||
* only for hardware decoding.
|
||||
*
|
||||
* You can communicate these handles to libmpv by adding a pseudo-OpenGL
|
||||
* extension "GL_MP_MPGetNativeDisplay" to the additional extension string when
|
||||
* calling mpv_opengl_cb_init_gl(). The get_proc_address callback should resolve
|
||||
* a function named "glMPGetNativeDisplay", which has the signature:
|
||||
*
|
||||
* void* GLAPIENTRY glMPGetNativeDisplay(const char* name)
|
||||
*
|
||||
* See below what names are defined. Usually, libmpv will use the native handle
|
||||
* up until mpv_opengl_cb_uninit_gl() is called. If the name is not anything
|
||||
* you know/expected, return NULL from the function.
|
||||
*
|
||||
* Windowing system interop on Linux
|
||||
* ---------------------------------
|
||||
*
|
||||
* The new VAAPI OpenGL interop requires an EGL context. EGL provides no way
|
||||
* to query the X11 Display associated to a specific EGL context, so this API
|
||||
* is used to pass it through.
|
||||
*
|
||||
* glMPGetNativeDisplay("x11") should return a X11 "Display*", which then will
|
||||
* be used to create the hardware decoder state. (On GLX, this is not needed.)
|
||||
*
|
||||
* Windowing system interop on MS win32
|
||||
* ------------------------------------
|
||||
*
|
||||
* If OpenGL switches to fullscreen, most players give it access GPU access,
|
||||
* which means DXVA2 hardware decoding in mpv won't work. This can be worked
|
||||
@@ -117,14 +145,14 @@ extern "C" {
|
||||
* create a decoder. The device can be either the real device used for display,
|
||||
* or a "blank" device created before switching to fullscreen.
|
||||
*
|
||||
* You can do this by adding "GL_MP_D3D_interfaces" to the additional extension
|
||||
* string when calling mpv_opengl_cb_init_gl(). The get_proc_address callback
|
||||
* should resolve a function named "glMPGetD3DInterface", which has the
|
||||
* signature: "void* __stdcall glMPGetD3DInterface(const char* name)". If
|
||||
* name is "IDirect3DDevice9", it should return a IDirect3DDevice9 pointer
|
||||
* (or NULL if not available). libmpv will release this interface when it is
|
||||
* done with it (usually when mpv_opengl_cb_uninit_gl() is called). New
|
||||
* interface names can be added in the future.
|
||||
* You can provide glMPGetNativeDisplay as described in the previous section.
|
||||
* If it is called with name set to "IDirect3DDevice9", it should return a
|
||||
* IDirect3DDevice9 pointer (or NULL if not available). libmpv will release
|
||||
* this interface when it is done with it.
|
||||
*
|
||||
* In previous libmpv releases, this used "GL_MP_D3D_interfaces" and
|
||||
* "glMPGetD3DInterface". This is deprecated; use glMPGetNativeDisplay instead
|
||||
* (the semantics are 100% compatible).
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
+11
-8
@@ -84,7 +84,6 @@ const struct m_opt_choice_alternatives mp_hwdec_names[] = {
|
||||
{"no", HWDEC_NONE},
|
||||
{"auto", HWDEC_AUTO},
|
||||
{"vdpau", HWDEC_VDPAU},
|
||||
{"vda", HWDEC_VDA},
|
||||
{"videotoolbox",HWDEC_VIDEOTOOLBOX},
|
||||
{"vaapi", HWDEC_VAAPI},
|
||||
{"vaapi-copy", HWDEC_VAAPI_COPY},
|
||||
@@ -132,7 +131,7 @@ const m_option_t mp_opts[] = {
|
||||
{"belownormal", BELOW_NORMAL_PRIORITY_CLASS},
|
||||
{"idle", IDLE_PRIORITY_CLASS})),
|
||||
#endif
|
||||
OPT_FLAG("config", load_config, CONF_GLOBAL | CONF_NOCFG | CONF_PRE_PARSE),
|
||||
OPT_FLAG("config", load_config, CONF_GLOBAL | CONF_PRE_PARSE),
|
||||
OPT_STRING("config-dir", force_configdir,
|
||||
CONF_GLOBAL | CONF_NOCFG | CONF_PRE_PARSE),
|
||||
OPT_STRINGLIST("reset-on-next-file", reset_options, M_OPT_GLOBAL),
|
||||
@@ -186,6 +185,8 @@ const m_option_t mp_opts[] = {
|
||||
{"http", 3})),
|
||||
OPT_FLAG("tls-verify", network_tls_verify, 0),
|
||||
OPT_STRING("tls-ca-file", network_tls_ca_file, M_OPT_FILE),
|
||||
OPT_STRING("tls-cert-file", network_tls_cert_file, M_OPT_FILE),
|
||||
OPT_STRING("tls-key-file", network_tls_key_file, M_OPT_FILE),
|
||||
OPT_DOUBLE("network-timeout", network_timeout, M_OPT_MIN, .min = 0),
|
||||
|
||||
// ------------------------- demuxer options --------------------
|
||||
@@ -373,7 +374,6 @@ const m_option_t mp_opts[] = {
|
||||
OPT_FLOATRANGE("osd-bar-w", osd_bar_w, 0, 1, 100),
|
||||
OPT_FLOATRANGE("osd-bar-h", osd_bar_h, 0, 0.1, 50),
|
||||
OPT_SUBSTRUCT("osd", osd_style, osd_style_conf, 0),
|
||||
OPT_FLAG("use-text-osd", use_text_osd, CONF_GLOBAL),
|
||||
OPT_SUBSTRUCT("sub-text", sub_text_style, sub_style_conf, 0),
|
||||
OPT_FLAG("sub-clear-on-seek", sub_clear_on_seek, 0),
|
||||
|
||||
@@ -384,6 +384,7 @@ const m_option_t mp_opts[] = {
|
||||
OPT_SETTINGSLIST("ao-defaults", ao_defs, 0, &ao_obj_list),
|
||||
OPT_STRING("audio-device", audio_device, 0),
|
||||
OPT_STRING("audio-client-name", audio_client_name, 0),
|
||||
OPT_FLAG("audio-fallback-to-null", ao_null_fallback, 0),
|
||||
OPT_CHOICE("force-window", force_vo, 0,
|
||||
({"no", 0}, {"yes", 1}, {"immediate", 2})),
|
||||
OPT_FLAG("ontop", vo.ontop, M_OPT_FIXED),
|
||||
@@ -464,6 +465,8 @@ const m_option_t mp_opts[] = {
|
||||
OPT_INTRANGE("contrast", gamma_contrast, 0, -100, 100),
|
||||
OPT_INTRANGE("hue", gamma_hue, 0, -100, 100),
|
||||
OPT_INTRANGE("gamma", gamma_gamma, 0, -100, 100),
|
||||
OPT_CHOICE_C("video-output-levels", video_output_levels, 0,
|
||||
mp_csp_levels_names),
|
||||
OPT_FLAG("keepaspect", vo.keepaspect, 0),
|
||||
OPT_FLAG("keepaspect-window", vo.keepaspect_window, 0),
|
||||
|
||||
@@ -519,14 +522,13 @@ const m_option_t mp_opts[] = {
|
||||
|
||||
// a-v sync stuff:
|
||||
OPT_FLAG("correct-pts", correct_pts, 0),
|
||||
OPT_CHOICE("pts-association-mode", user_pts_assoc_mode, 0,
|
||||
({"auto", 0}, {"decoder", 1}, {"sort", 2})),
|
||||
OPT_FLAG("initial-audio-sync", initial_audio_sync, 0),
|
||||
OPT_CHOICE("video-sync", video_sync, 0,
|
||||
({"audio", VS_DEFAULT},
|
||||
{"display-resample", VS_DISP_RESAMPLE},
|
||||
{"display-resample-vdrop", VS_DISP_RESAMPLE_VDROP},
|
||||
{"display-resample-desync", VS_DISP_RESAMPLE_NONE},
|
||||
{"display-adrop", VS_DISP_ADROP},
|
||||
{"display-vdrop", VS_DISP_VDROP},
|
||||
{"display-desync", VS_DISP_NONE},
|
||||
{"desync", VS_NONE})),
|
||||
@@ -534,6 +536,8 @@ const m_option_t mp_opts[] = {
|
||||
M_OPT_MIN, .min = 0),
|
||||
OPT_DOUBLE("video-sync-max-audio-change", sync_max_audio_change,
|
||||
M_OPT_MIN | M_OPT_MAX, .min = 0, .max = 1),
|
||||
OPT_DOUBLE("video-sync-adrop-size", sync_audio_drop_size,
|
||||
M_OPT_MIN | M_OPT_MAX, .min = 0, .max = 1),
|
||||
OPT_CHOICE("hr-seek", hr_seek, 0,
|
||||
({"no", -1}, {"absolute", 0}, {"yes", 1}, {"always", 1})),
|
||||
OPT_FLOAT("hr-seek-demuxer-offset", hr_seek_demuxer_offset, 0),
|
||||
@@ -713,11 +717,10 @@ const struct MPOpts mp_default_opts = {
|
||||
.sub_use_margins = 1,
|
||||
.ass_scale_with_window = 0,
|
||||
.sub_scale_with_window = 1,
|
||||
.use_text_osd = 1,
|
||||
#if HAVE_LUA
|
||||
.lua_load_osc = 1,
|
||||
.lua_load_ytdl = 1,
|
||||
.lua_ytdl_format = "best",
|
||||
.lua_ytdl_format = NULL,
|
||||
.lua_ytdl_raw_options = NULL,
|
||||
#endif
|
||||
.auto_load_scripts = 1,
|
||||
@@ -728,6 +731,7 @@ const struct MPOpts mp_default_opts = {
|
||||
.hr_seek_framedrop = 1,
|
||||
.sync_max_video_change = 1,
|
||||
.sync_max_audio_change = 0.125,
|
||||
.sync_audio_drop_size = 0.020,
|
||||
.load_config = 1,
|
||||
.position_resume = 1,
|
||||
.stream_cache = {
|
||||
@@ -752,7 +756,6 @@ const struct MPOpts mp_default_opts = {
|
||||
.edition_id = -1,
|
||||
.default_max_pts_correction = -1,
|
||||
.correct_pts = 1,
|
||||
.user_pts_assoc_mode = 1,
|
||||
.initial_audio_sync = 1,
|
||||
.frame_dropping = 1,
|
||||
.term_osd = 2,
|
||||
|
||||
+5
-2
@@ -81,6 +81,7 @@ typedef struct MPOpts {
|
||||
struct m_obj_settings *audio_driver_list, *ao_defs;
|
||||
char *audio_device;
|
||||
char *audio_client_name;
|
||||
int ao_null_fallback;
|
||||
int force_vo;
|
||||
int softvol;
|
||||
float mixer_init_volume;
|
||||
@@ -103,6 +104,7 @@ typedef struct MPOpts {
|
||||
int gamma_contrast;
|
||||
int gamma_saturation;
|
||||
int gamma_hue;
|
||||
int video_output_levels;
|
||||
|
||||
int stop_screensaver;
|
||||
int cursor_autohide_delay;
|
||||
@@ -142,11 +144,11 @@ typedef struct MPOpts {
|
||||
int chapterrange[2];
|
||||
int edition_id;
|
||||
int correct_pts;
|
||||
int user_pts_assoc_mode;
|
||||
int initial_audio_sync;
|
||||
int video_sync;
|
||||
double sync_max_video_change;
|
||||
double sync_max_audio_change;
|
||||
double sync_audio_drop_size;
|
||||
int hr_seek;
|
||||
float hr_seek_demuxer_offset;
|
||||
int hr_seek_framedrop;
|
||||
@@ -233,7 +235,6 @@ typedef struct MPOpts {
|
||||
char **sub_paths;
|
||||
int sub_auto;
|
||||
int audiofile_auto;
|
||||
int use_text_osd;
|
||||
int osd_bar_visible;
|
||||
float osd_bar_align_x;
|
||||
float osd_bar_align_y;
|
||||
@@ -276,6 +277,8 @@ typedef struct MPOpts {
|
||||
char **network_http_header_fields;
|
||||
int network_tls_verify;
|
||||
char *network_tls_ca_file;
|
||||
char *network_tls_cert_file;
|
||||
char *network_tls_key_file;
|
||||
double network_timeout;
|
||||
|
||||
struct tv_params *tv_params;
|
||||
|
||||
+7
-1
@@ -76,10 +76,12 @@ static const char *mp_get_platform_path(void *talloc_ctx,
|
||||
const char *force_configdir = getenv("MPV_HOME");
|
||||
if (global->opts->force_configdir && global->opts->force_configdir[0])
|
||||
force_configdir = global->opts->force_configdir;
|
||||
if (!global->opts->load_config)
|
||||
force_configdir = "";
|
||||
if (force_configdir) {
|
||||
for (int n = 0; n < MP_ARRAY_SIZE(config_dirs); n++) {
|
||||
if (strcmp(config_dirs[n], type) == 0)
|
||||
return n == 0 ? force_configdir : NULL;
|
||||
return (n == 0 && force_configdir[0]) ? force_configdir : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +259,10 @@ char *mp_path_join(void *talloc_ctx, const char *p1, const char *p2)
|
||||
|
||||
char *mp_getcwd(void *talloc_ctx)
|
||||
{
|
||||
char *e_wd = getenv("PWD");
|
||||
if (e_wd)
|
||||
return talloc_strdup(talloc_ctx, e_wd);
|
||||
|
||||
char *wd = talloc_array(talloc_ctx, char, 20);
|
||||
while (getcwd(wd, talloc_get_size(wd)) == NULL) {
|
||||
if (errno != ERANGE) {
|
||||
|
||||
+74
-6
@@ -29,6 +29,7 @@
|
||||
#include "common/encode.h"
|
||||
#include "options/options.h"
|
||||
#include "common/common.h"
|
||||
#include "osdep/timer.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audio.h"
|
||||
@@ -153,6 +154,7 @@ void reset_audio_state(struct MPContext *mpctx)
|
||||
mp_audio_buffer_clear(mpctx->ao_buffer);
|
||||
mpctx->audio_status = mpctx->d_audio ? STATUS_SYNCING : STATUS_EOF;
|
||||
mpctx->delay = 0;
|
||||
mpctx->audio_stat_start = 0;
|
||||
}
|
||||
|
||||
void uninit_audio_out(struct MPContext *mpctx)
|
||||
@@ -260,11 +262,15 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
}
|
||||
|
||||
if (!mpctx->ao) {
|
||||
bool spdif_fallback = af_fmt_is_spdif(afs->output.format) &&
|
||||
mpctx->d_audio->spdif_passthrough;
|
||||
bool ao_null_fallback = opts->ao_null_fallback && !spdif_fallback;
|
||||
|
||||
mp_chmap_remove_useless_channels(&afs->output.channels,
|
||||
&opts->audio_output_channels);
|
||||
mp_audio_set_channels(&afs->output, &afs->output.channels);
|
||||
|
||||
mpctx->ao = ao_init_best(mpctx->global, mpctx->input,
|
||||
mpctx->ao = ao_init_best(mpctx->global, ao_null_fallback, mpctx->input,
|
||||
mpctx->encode_lavc_ctx, afs->output.rate,
|
||||
afs->output.format, afs->output.channels);
|
||||
|
||||
@@ -283,10 +289,9 @@ void reinit_audio_chain(struct MPContext *mpctx)
|
||||
|
||||
if (!mpctx->ao) {
|
||||
// If spdif was used, try to fallback to PCM.
|
||||
if (af_fmt_is_spdif(afs->output.format) &&
|
||||
mpctx->d_audio->spdif_passthrough)
|
||||
{
|
||||
if (spdif_fallback) {
|
||||
mpctx->d_audio->spdif_passthrough = false;
|
||||
mpctx->d_audio->spdif_failed = true;
|
||||
if (!audio_init_best_codec(mpctx->d_audio))
|
||||
goto init_error;
|
||||
reset_audio_state(mpctx);
|
||||
@@ -400,11 +405,31 @@ static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags
|
||||
if (played > 0) {
|
||||
mpctx->shown_aframes += played;
|
||||
mpctx->delay += played / real_samplerate;
|
||||
mpctx->written_audio += played / (double)out_format.rate;
|
||||
return played;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_audio_stats(struct MPContext *mpctx)
|
||||
{
|
||||
if (!mp_msg_test(mpctx->log, MSGL_STATS))
|
||||
return;
|
||||
if (mpctx->audio_status != STATUS_PLAYING || !mpctx->ao || mpctx->paused) {
|
||||
mpctx->audio_stat_start = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
double delay = ao_get_delay(mpctx->ao);
|
||||
if (!mpctx->audio_stat_start) {
|
||||
mpctx->audio_stat_start = mp_time_us();
|
||||
mpctx->written_audio = delay;
|
||||
}
|
||||
double current_audio = mpctx->written_audio - delay;
|
||||
double current_time = (mp_time_us() - mpctx->audio_stat_start) / 1e6;
|
||||
MP_STATS(mpctx, "value %f ao-dev", current_audio - current_time);
|
||||
}
|
||||
|
||||
// Return the number of samples that must be skipped or prepended to reach the
|
||||
// target audio pts after a seek (for A/V sync or hr-seek).
|
||||
// Return value (*skip):
|
||||
@@ -441,7 +466,7 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
|
||||
if (mpctx->video_status < STATUS_READY)
|
||||
return false; // wait until we know a video PTS
|
||||
if (mpctx->video_next_pts != MP_NOPTS_VALUE)
|
||||
sync_pts = mpctx->video_next_pts - (opts->audio_delay - mpctx->delay);
|
||||
sync_pts = mpctx->video_next_pts - opts->audio_delay;
|
||||
} else if (mpctx->hrseek_active) {
|
||||
sync_pts = mpctx->hrseek_pts;
|
||||
}
|
||||
@@ -468,11 +493,23 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
struct dec_audio *d_audio = mpctx->d_audio;
|
||||
|
||||
dump_audio_stats(mpctx);
|
||||
|
||||
if (mpctx->ao && ao_query_and_reset_events(mpctx->ao, AO_EVENT_RELOAD)) {
|
||||
ao_reset(mpctx->ao);
|
||||
uninit_audio_out(mpctx);
|
||||
if (d_audio)
|
||||
if (d_audio) {
|
||||
if (mpctx->d_audio->spdif_failed) {
|
||||
mpctx->d_audio->spdif_failed = false;
|
||||
mpctx->d_audio->spdif_passthrough = true;
|
||||
if (!audio_init_best_codec(mpctx->d_audio)) {
|
||||
MP_ERR(mpctx, "Error reinitializing audio.\n");
|
||||
error_on_track(mpctx, mpctx->current_track[0][STREAM_AUDIO]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mpctx->audio_status = STATUS_SYNCING;
|
||||
}
|
||||
}
|
||||
|
||||
if (!d_audio)
|
||||
@@ -516,6 +553,20 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
playsize = MPMAX(1, playsize + skip); // silence will be prepended
|
||||
}
|
||||
|
||||
int skip_duplicate = 0; // >0: skip, <0: duplicate
|
||||
if (mpctx->display_sync_active && opts->video_sync == VS_DISP_ADROP &&
|
||||
fabs(mpctx->last_av_difference) >= opts->sync_audio_drop_size &&
|
||||
mpctx->audio_status == STATUS_PLAYING)
|
||||
{
|
||||
int samples = ceil(opts->sync_audio_drop_size * play_samplerate);
|
||||
int align = af_format_sample_alignment(out_format.format);
|
||||
samples = (samples + align / 2) / align * align;
|
||||
|
||||
skip_duplicate = mpctx->last_av_difference >= 0 ? -samples : samples;
|
||||
|
||||
playsize = MPMAX(playsize, samples);
|
||||
}
|
||||
|
||||
int status = AD_OK;
|
||||
bool working = false;
|
||||
if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) {
|
||||
@@ -563,6 +614,21 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
end_sync = true;
|
||||
}
|
||||
|
||||
if (skip_duplicate) {
|
||||
int max = mp_audio_buffer_samples(mpctx->ao_buffer);
|
||||
if (abs(skip_duplicate) > max)
|
||||
skip_duplicate = skip_duplicate >= 0 ? max : -max;
|
||||
mpctx->last_av_difference += skip_duplicate / play_samplerate;
|
||||
if (skip_duplicate >= 0) {
|
||||
mp_audio_buffer_skip(mpctx->ao_buffer, skip_duplicate);
|
||||
MP_STATS(mpctx, "drop-audio");
|
||||
} else {
|
||||
mp_audio_buffer_duplicate(mpctx->ao_buffer, -skip_duplicate);
|
||||
MP_STATS(mpctx, "duplicate-audio");
|
||||
}
|
||||
MP_VERBOSE(mpctx, "audio skip_duplicate=%d\n", skip_duplicate);
|
||||
}
|
||||
|
||||
if (mpctx->audio_status == STATUS_SYNCING) {
|
||||
if (end_sync)
|
||||
mpctx->audio_status = STATUS_FILLING;
|
||||
@@ -617,6 +683,8 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
|
||||
assert(played >= 0 && played <= data.samples);
|
||||
mp_audio_buffer_skip(mpctx->ao_buffer, played);
|
||||
|
||||
dump_audio_stats(mpctx);
|
||||
|
||||
mpctx->audio_status = STATUS_PLAYING;
|
||||
if (audio_eof && !playsize) {
|
||||
mpctx->audio_status = STATUS_DRAINING;
|
||||
|
||||
+1
-1
@@ -1601,7 +1601,7 @@ static const char *const err_table[] = {
|
||||
[-MPV_ERROR_LOADING_FAILED] = "loading failed",
|
||||
[-MPV_ERROR_AO_INIT_FAILED] = "audio output initialization failed",
|
||||
[-MPV_ERROR_VO_INIT_FAILED] = "audio output initialization failed",
|
||||
[-MPV_ERROR_NOTHING_TO_PLAY] = "no audio or video data found",
|
||||
[-MPV_ERROR_NOTHING_TO_PLAY] = "no audio or video data played",
|
||||
[-MPV_ERROR_UNKNOWN_FORMAT] = "unrecognized file format",
|
||||
[-MPV_ERROR_UNSUPPORTED] = "not supported",
|
||||
[-MPV_ERROR_NOT_IMPLEMENTED] = "operation not implemented",
|
||||
|
||||
+45
-25
@@ -484,6 +484,9 @@ static int mp_property_stream_end(void *ctx, struct m_property *prop,
|
||||
// Assumes prop is the type of the actual property.
|
||||
static int property_time(int action, void *arg, double time)
|
||||
{
|
||||
if (time == MP_NOPTS_VALUE)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
const struct m_option time_type = {.type = CONF_TYPE_TIME};
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
@@ -652,6 +655,9 @@ static bool time_remaining(MPContext *mpctx, double *remaining)
|
||||
double len = get_time_length(mpctx);
|
||||
double playback = get_playback_time(mpctx);
|
||||
|
||||
if (playback == MP_NOPTS_VALUE)
|
||||
return false;
|
||||
|
||||
*remaining = len - playback;
|
||||
|
||||
return len >= 0;
|
||||
@@ -1187,21 +1193,31 @@ static int mp_property_chapter_metadata(void *ctx, struct m_property *prop,
|
||||
return tag_property(action, arg, mpctx->chapters[chapter].metadata);
|
||||
}
|
||||
|
||||
static int mp_property_vf_metadata(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
static int mp_property_filter_metadata(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
if (!(mpctx->d_video && mpctx->d_video->vfilter))
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
struct vf_chain *vf = mpctx->d_video->vfilter;
|
||||
const char *type = prop->priv;
|
||||
|
||||
if (action == M_PROPERTY_KEY_ACTION) {
|
||||
struct m_property_action_arg *ka = arg;
|
||||
bstr key;
|
||||
char *rem;
|
||||
m_property_split_path(ka->key, &key, &rem);
|
||||
struct mp_tags vf_metadata = {0};
|
||||
switch (vf_control_by_label(vf, VFCTRL_GET_METADATA, &vf_metadata, key)) {
|
||||
struct mp_tags metadata = {0};
|
||||
int res = CONTROL_UNKNOWN;
|
||||
if (strcmp(type, "vf") == 0) {
|
||||
if (!(mpctx->d_video && mpctx->d_video->vfilter))
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
struct vf_chain *vf = mpctx->d_video->vfilter;
|
||||
res = vf_control_by_label(vf, VFCTRL_GET_METADATA, &metadata, key);
|
||||
} else if (strcmp(type, "af") == 0) {
|
||||
if (!(mpctx->d_audio && mpctx->d_audio->afilter))
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
struct af_stream *af = mpctx->d_audio->afilter;
|
||||
res = af_control_by_label(af, AF_CONTROL_GET_METADATA, &metadata, key);
|
||||
}
|
||||
switch (res) {
|
||||
case CONTROL_UNKNOWN:
|
||||
return M_PROPERTY_UNKNOWN;
|
||||
case CONTROL_NA: // empty
|
||||
@@ -1209,9 +1225,9 @@ static int mp_property_vf_metadata(void *ctx, struct m_property *prop,
|
||||
if (strlen(rem)) {
|
||||
struct m_property_action_arg next_ka = *ka;
|
||||
next_ka.key = rem;
|
||||
return tag_property(M_PROPERTY_KEY_ACTION, &next_ka, &vf_metadata);
|
||||
return tag_property(M_PROPERTY_KEY_ACTION, &next_ka, &metadata);
|
||||
} else {
|
||||
return tag_property(ka->action, ka->arg, &vf_metadata);
|
||||
return tag_property(ka->action, ka->arg, &metadata);
|
||||
}
|
||||
return M_PROPERTY_OK;
|
||||
default:
|
||||
@@ -1601,7 +1617,7 @@ static int mp_property_audio_device(void *ctx, struct m_property *prop,
|
||||
struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
|
||||
for (int n = 0; n < list->num_devices; n++) {
|
||||
struct ao_device_desc *dev = &list->devices[n];
|
||||
if (dev->name && strcmp(dev->name, mpctx->opts->audio_device)) {
|
||||
if (dev->name && strcmp(dev->name, mpctx->opts->audio_device) == 0) {
|
||||
*(char **)arg = talloc_strdup(NULL, dev->desc ? dev->desc : "?");
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
@@ -2360,18 +2376,19 @@ static int mp_property_framedrop(void *ctx, struct m_property *prop,
|
||||
static int mp_property_video_color(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
const char *name = prop->priv ? prop->priv : prop->name;
|
||||
MPContext *mpctx = ctx;
|
||||
if (!mpctx->d_video)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_SET: {
|
||||
if (video_set_colors(mpctx->d_video, prop->name, *(int *) arg) <= 0)
|
||||
if (video_set_colors(mpctx->d_video, name, *(int *) arg) <= 0)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
break;
|
||||
}
|
||||
case M_PROPERTY_GET:
|
||||
if (video_get_colors(mpctx->d_video, prop->name, (int *)arg) <= 0)
|
||||
if (video_get_colors(mpctx->d_video, name, (int *)arg) <= 0)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
// Write new value to option variable
|
||||
mp_property_generic_option(mpctx, prop, M_PROPERTY_SET, arg);
|
||||
@@ -2430,8 +2447,6 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
|
||||
SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.colorspace))},
|
||||
{"colorlevels",
|
||||
SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.colorlevels))},
|
||||
{"outputlevels",
|
||||
SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.outputlevels))},
|
||||
{"primaries",
|
||||
SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.primaries))},
|
||||
{"gamma",
|
||||
@@ -3072,11 +3087,18 @@ static int mp_property_packet_bitrate(void *ctx, struct m_property *prop,
|
||||
int type = (uintptr_t)prop->priv & ~0x100;
|
||||
bool old = (uintptr_t)prop->priv & 0x100;
|
||||
|
||||
if (!mpctx->demuxer)
|
||||
struct demuxer *demuxer = NULL;
|
||||
if (mpctx->current_track[0][type])
|
||||
demuxer = mpctx->current_track[0][type]->demuxer;
|
||||
if (!demuxer)
|
||||
demuxer = mpctx->demuxer;
|
||||
if (!demuxer)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
double r[STREAM_TYPE_COUNT];
|
||||
if (demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_BITRATE_STATS, &r) < 1)
|
||||
if (demux_control(demuxer, DEMUXER_CTRL_GET_BITRATE_STATS, &r) < 1)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
if (r[type] < 0)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
// r[type] is in bytes/second -> bits
|
||||
@@ -3379,7 +3401,8 @@ static const struct m_property mp_properties[] = {
|
||||
{"metadata", mp_property_metadata},
|
||||
{"filtered-metadata", mp_property_filtered_metadata},
|
||||
{"chapter-metadata", mp_property_chapter_metadata},
|
||||
{"vf-metadata", mp_property_vf_metadata},
|
||||
{"vf-metadata", mp_property_filter_metadata, .priv = "vf"},
|
||||
{"af-metadata", mp_property_filter_metadata, .priv = "af"},
|
||||
{"pause", mp_property_pause},
|
||||
{"core-idle", mp_property_core_idle},
|
||||
{"eof-reached", mp_property_eof_reached},
|
||||
@@ -3395,7 +3418,6 @@ static const struct m_property mp_properties[] = {
|
||||
{"demuxer-cache-idle", mp_property_demuxer_cache_idle},
|
||||
{"cache-buffering-state", mp_property_cache_buffering},
|
||||
{"paused-for-cache", mp_property_paused_for_cache},
|
||||
{"pts-association-mode", mp_property_generic_option},
|
||||
{"hr-seek", mp_property_generic_option},
|
||||
{"clock", mp_property_clock},
|
||||
{"seekable", mp_property_seekable},
|
||||
@@ -3442,6 +3464,8 @@ static const struct m_property mp_properties[] = {
|
||||
{"contrast", mp_property_video_color},
|
||||
{"saturation", mp_property_video_color},
|
||||
{"hue", mp_property_video_color},
|
||||
{"video-output-levels", mp_property_video_color,
|
||||
.priv = (void *)"output-levels"},
|
||||
{"panscan", panscan_property_helper},
|
||||
{"video-zoom", panscan_property_helper},
|
||||
{"video-align-x", panscan_property_helper},
|
||||
@@ -3552,7 +3576,6 @@ static const struct m_property mp_properties[] = {
|
||||
M_PROPERTY_ALIAS("sub", "sid"),
|
||||
M_PROPERTY_ALIAS("colormatrix", "video-params/colormatrix"),
|
||||
M_PROPERTY_ALIAS("colormatrix-input-range", "video-params/colorlevels"),
|
||||
M_PROPERTY_ALIAS("colormatrix-output-range", "video-params/outputlevels"),
|
||||
M_PROPERTY_ALIAS("colormatrix-primaries", "video-params/primaries"),
|
||||
M_PROPERTY_ALIAS("colormatrix-gamma", "video-params/gamma"),
|
||||
|
||||
@@ -3723,7 +3746,6 @@ static const struct property_osd_display {
|
||||
{ "chapter", .seek_msg = OSD_SEEK_INFO_CHAPTER_TEXT,
|
||||
.seek_bar = OSD_SEEK_INFO_BAR },
|
||||
{ "edition", .seek_msg = OSD_SEEK_INFO_EDITION },
|
||||
{ "pts-association-mode", "PTS association mode" },
|
||||
{ "hr-seek", "hr-seek" },
|
||||
{ "speed", "Speed" },
|
||||
{ "clock", "Clock" },
|
||||
@@ -4286,13 +4308,11 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||
case MP_CMD_ADD:
|
||||
case MP_CMD_CYCLE:
|
||||
{
|
||||
char *property = cmd->args[0].v.s;
|
||||
struct m_property_switch_arg s = {
|
||||
.inc = 1,
|
||||
.inc = cmd->args[1].v.d * cmd->scale,
|
||||
.wrap = cmd->id == MP_CMD_CYCLE,
|
||||
};
|
||||
if (cmd->args[1].v.d)
|
||||
s.inc = cmd->args[1].v.d * cmd->scale;
|
||||
char *property = cmd->args[0].v.s;
|
||||
if (cmd->repeated && !check_property_autorepeat(property, mpctx)) {
|
||||
MP_VERBOSE(mpctx, "Dropping command '%.*s' from auto-repeated key.\n",
|
||||
BSTR_P(cmd->original));
|
||||
@@ -4581,7 +4601,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||
|
||||
case MP_CMD_STOP:
|
||||
playlist_clear(mpctx->playlist);
|
||||
if (!mpctx->stop_play)
|
||||
if (mpctx->stop_play != PT_QUIT)
|
||||
mpctx->stop_play = PT_STOP;
|
||||
break;
|
||||
|
||||
|
||||
@@ -59,8 +59,6 @@ static void load_all_cfgfiles(struct MPContext *mpctx, char *section,
|
||||
void mp_parse_cfgfiles(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
if (!opts->load_config)
|
||||
return;
|
||||
|
||||
mp_mk_config_dir(mpctx->global, "");
|
||||
|
||||
@@ -288,10 +286,6 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
double pos = get_current_time(mpctx);
|
||||
if (pos == MP_NOPTS_VALUE)
|
||||
goto exit;
|
||||
|
||||
mp_mk_config_dir(mpctx->global, MP_WATCH_LATER_CONF);
|
||||
|
||||
conffile = mp_get_playback_resume_config_filename(mpctx, filename);
|
||||
@@ -309,7 +303,9 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
|
||||
write_name[n] = (unsigned char)filename[n] < 32 ? '_' : filename[n];
|
||||
fprintf(file, "# %s\n", write_name);
|
||||
}
|
||||
fprintf(file, "start=%f\n", pos);
|
||||
double pos = get_current_time(mpctx);
|
||||
if (pos != MP_NOPTS_VALUE)
|
||||
fprintf(file, "start=%f\n", pos);
|
||||
for (int i = 0; backup_properties[i]; i++) {
|
||||
const char *pname = backup_properties[i];
|
||||
char *val = NULL;
|
||||
|
||||
+6
-1
@@ -84,6 +84,7 @@ enum video_sync {
|
||||
VS_DISP_RESAMPLE,
|
||||
VS_DISP_RESAMPLE_VDROP,
|
||||
VS_DISP_RESAMPLE_NONE,
|
||||
VS_DISP_ADROP,
|
||||
VS_DISP_VDROP,
|
||||
VS_DISP_NONE,
|
||||
VS_NONE,
|
||||
@@ -92,6 +93,7 @@ enum video_sync {
|
||||
#define VS_IS_DISP(x) ((x) == VS_DISP_RESAMPLE || \
|
||||
(x) == VS_DISP_RESAMPLE_VDROP || \
|
||||
(x) == VS_DISP_RESAMPLE_NONE || \
|
||||
(x) == VS_DISP_ADROP || \
|
||||
(x) == VS_DISP_VDROP || \
|
||||
(x) == VS_DISP_NONE)
|
||||
|
||||
@@ -302,6 +304,9 @@ typedef struct MPContext {
|
||||
double last_vo_pts;
|
||||
// Video PTS, or audio PTS if video has ended.
|
||||
double playback_pts;
|
||||
// audio stats only
|
||||
int64_t audio_stat_start;
|
||||
double written_audio;
|
||||
|
||||
int last_chapter;
|
||||
|
||||
@@ -488,7 +493,7 @@ void execute_queued_seek(struct MPContext *mpctx);
|
||||
void run_playloop(struct MPContext *mpctx);
|
||||
void mp_idle(struct MPContext *mpctx);
|
||||
void idle_loop(struct MPContext *mpctx);
|
||||
void handle_force_window(struct MPContext *mpctx, bool reconfig);
|
||||
int handle_force_window(struct MPContext *mpctx, bool force);
|
||||
void add_frame_pts(struct MPContext *mpctx, double pts);
|
||||
int get_past_frame_durations(struct MPContext *mpctx, double *fd, int num);
|
||||
void seek_to_last_frame(struct MPContext *mpctx);
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
#include "misc/ctype.h"
|
||||
#include "options/options.h"
|
||||
#include "options/path.h"
|
||||
#include "sub/find_subfiles.h"
|
||||
#include "external_files.h"
|
||||
|
||||
static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "srt",
|
||||
"smi", "rt", "txt", "ssa", "aqt", "jss",
|
||||
"js", "ass", "mks", "vtt", "sup", NULL};
|
||||
|
||||
static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac",
|
||||
"ogg", "m4a", "ac3", NULL};
|
||||
"ogg", "m4a", "ac3", "opus", "wav",
|
||||
NULL};
|
||||
|
||||
static bool test_ext_list(bstr ext, const char *const *list)
|
||||
{
|
||||
File renamed without changes.
Loaded 100 of 201 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user