Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d9f8f75ea | ||
|
|
2ffdc0da13 | ||
|
|
7ae6484cd4 | ||
|
|
08d144138b | ||
|
|
58b511d947 | ||
|
|
9b8b2933f3 | ||
|
|
24c05b2a82 | ||
|
|
1412b0171d | ||
|
|
5c1c99b858 | ||
|
|
f66e320d7b | ||
|
|
fa2381765e | ||
|
|
f220617267 | ||
|
|
381843eb88 | ||
|
|
9258d0461e | ||
|
|
e2f59fcabd | ||
|
|
5b76ce3865 | ||
|
|
1b29964a3e | ||
|
|
180fed4bc4 | ||
|
|
fb3bf1ae81 | ||
|
|
f842b3f2b2 | ||
|
|
e4f6235a47 | ||
|
|
89df07aa6d | ||
|
|
04a8dd38d5 | ||
|
|
72e881b60d | ||
|
|
45b99d9799 | ||
|
|
b784083fb5 | ||
|
|
d7cf596e2a | ||
|
|
a2e38ca657 | ||
|
|
3e410f423d | ||
|
|
061f509757 | ||
|
|
03bfc2b3fd | ||
|
|
61ebe6137e | ||
|
|
4ea05577bd | ||
|
|
f23827d557 | ||
|
|
c2592be4f0 | ||
|
|
053196de84 | ||
|
|
50e131b43e | ||
|
|
b9e4eefdb1 | ||
|
|
6b9aee20bd | ||
|
|
b0cb2977ed | ||
|
|
39451732e9 | ||
|
|
0fee6537bb | ||
|
|
cbfb6de667 | ||
|
|
34fc0720f7 | ||
|
|
3273db1ef7 | ||
|
|
aeaa1767e9 | ||
|
|
295b6dc169 | ||
|
|
cdb25d5a21 | ||
|
|
0ec6df2ddf | ||
|
|
cbf7180c90 | ||
|
|
b4d1494336 | ||
|
|
acf6aef882 | ||
|
|
650af29471 | ||
|
|
e79de41b97 | ||
|
|
d778130dc4 | ||
|
|
4784ca32c9 | ||
|
|
387d5f55e6 | ||
|
|
da1918b894 | ||
|
|
3208f8c445 | ||
|
|
12d93fdfef | ||
|
|
1b1421866d | ||
|
|
17d031f88c | ||
|
|
8fd954ac8e | ||
|
|
fdf40743bc | ||
|
|
d6c27855d7 | ||
|
|
c3f7773138 | ||
|
|
d191de8564 | ||
|
|
d8f993705c | ||
|
|
ed116e8b06 | ||
|
|
07668e50de | ||
|
|
d23ffd243f | ||
|
|
9537e33057 | ||
|
|
5116c6c242 | ||
|
|
debbff76f9 | ||
|
|
09b7956ca5 | ||
|
|
1c9f30cca7 | ||
|
|
7aa933cc9e | ||
|
|
9c3c199558 | ||
|
|
fd7dde404d | ||
|
|
e977624d87 | ||
|
|
9ac86d9e99 | ||
|
|
8a8f65d73d | ||
|
|
1d45a3a163 | ||
|
|
546c0f0b25 | ||
|
|
c6d0e41335 | ||
|
|
7e5f707baf | ||
|
|
3eae8b7170 | ||
|
|
735a9c39d7 | ||
|
|
53c288d364 | ||
|
|
e12fae8ef5 | ||
|
|
7954017b56 | ||
|
|
bf927531aa | ||
|
|
429260a35c | ||
|
|
c2fa9f6629 | ||
|
|
7230d88c7e | ||
|
|
601fb2f93a | ||
|
|
81bf9a1963 | ||
|
|
308d72a02e | ||
|
|
b745c2d005 | ||
|
|
5b5a3d0c46 | ||
|
|
1f4a74cbed | ||
|
|
9fe076f02a | ||
|
|
9ce4526139 | ||
|
|
2cfc35892c | ||
|
|
f605b03f63 | ||
|
|
63a2024a8b | ||
|
|
14ff4cf318 | ||
|
|
709002a3b2 | ||
|
|
c99a966251 | ||
|
|
903bd1d893 | ||
|
|
4a0bbe256e | ||
|
|
60ca2d8f4d | ||
|
|
a0cee03394 | ||
|
|
58db0a55f3 | ||
|
|
54d1dae687 | ||
|
|
b12a287227 | ||
|
|
a4241f9de6 | ||
|
|
1cf229f462 | ||
|
|
bb005a385a | ||
|
|
dfbb4d03ee | ||
|
|
5332ecf651 | ||
|
|
68b7217d41 | ||
|
|
461c78e4d9 | ||
|
|
5e6c9963d8 | ||
|
|
34fac3bf68 | ||
|
|
7329101478 | ||
|
|
f64199fcdc | ||
|
|
f5fb1e915e | ||
|
|
771583c285 | ||
|
|
4047e7e6cb | ||
|
|
65e816b3fe | ||
|
|
116d1d0dfa | ||
|
|
70f4721574 | ||
|
|
c86b4790a8 | ||
|
|
30175538fe | ||
|
|
b99c67f9bb | ||
|
|
29467736cf | ||
|
|
be516022b6 | ||
|
|
95bb0bb671 | ||
|
|
580cf433bd | ||
|
|
5c8549ac68 | ||
|
|
e37ad620aa | ||
|
|
35810cb8b4 | ||
|
|
ea2b19f646 | ||
|
|
6c3d25e6f5 | ||
|
|
a522441bbe | ||
|
|
a173d3a2fb | ||
|
|
396756e58a | ||
|
|
c158e4641a | ||
|
|
f2c46bc1d1 | ||
|
|
02d0b2f31c | ||
|
|
6ef40cb910 | ||
|
|
60f5e53f60 | ||
|
|
d83a9f7f03 | ||
|
|
caaeb15318 | ||
|
|
26e0cce969 | ||
|
|
33a63d8e6e | ||
|
|
b41ece5ad4 | ||
|
|
5fd8660f5f | ||
|
|
11c044aa48 | ||
|
|
45e2345a7f | ||
|
|
ec2c6a17dc | ||
|
|
ba2a3f40ac | ||
|
|
95053ba78b | ||
|
|
20643b6c91 | ||
|
|
9e1ea9b3cd | ||
|
|
cfdeb9d8ce | ||
|
|
b2b1b848da | ||
|
|
7c2fb859ab | ||
|
|
d5b8b5b901 | ||
|
|
b951326a38 | ||
|
|
9ca1582953 | ||
|
|
2308eda2b8 | ||
|
|
8efc4b7e24 | ||
|
|
8c002b79d3 | ||
|
|
930c61b64c | ||
|
|
ac2047e02e | ||
|
|
9bfa38add6 | ||
|
|
3f6212cd8d | ||
|
|
e0b4daf3ad | ||
|
|
b44571abab | ||
|
|
955671f64a | ||
|
|
08116feec7 | ||
|
|
b6d8d5e05c | ||
|
|
b799bf0dbf | ||
|
|
893f4a0fee | ||
|
|
2e91d44e20 | ||
|
|
2dd819705d | ||
|
|
7c77f0b803 | ||
|
|
1273febc44 | ||
|
|
b5d253a5cb | ||
|
|
8bf57b8192 | ||
|
|
b0a3d38b4c | ||
|
|
a3b393d68c | ||
|
|
fc5df2b970 | ||
|
|
f93dd45ac1 | ||
|
|
d26a0ae111 | ||
|
|
5f80e3f91a | ||
|
|
f744aadb77 | ||
|
|
f790d6aafd | ||
|
|
369868e404 | ||
|
|
94fe57856d | ||
|
|
5bbf5ee103 | ||
|
|
302a04091b | ||
|
|
524db3384b | ||
|
|
e9b756c7ad | ||
|
|
564b957cc3 | ||
|
|
ae63702a2c | ||
|
|
e2a093df02 | ||
|
|
fe0ca7559c | ||
|
|
5be678386b | ||
|
|
256fea7655 | ||
|
|
49a0b61880 | ||
|
|
28fc13977e | ||
|
|
d0b525121a | ||
|
|
670f965f1a | ||
|
|
e1e8b07cfa | ||
|
|
c0fbab7a7c | ||
|
|
9cb1e2c58c | ||
|
|
76f9eede34 | ||
|
|
b578abe81b | ||
|
|
4ef531f815 | ||
|
|
3b5f28bd0f | ||
|
|
8c7a9b0cd0 | ||
|
|
f5af596237 | ||
|
|
5546af421f | ||
|
|
017b3fa9db | ||
|
|
348dfd93c4 | ||
|
|
94113e632f | ||
|
|
a1d3afb395 | ||
|
|
a6774d3a83 | ||
|
|
7791e25fa0 | ||
|
|
39609fc19a | ||
|
|
769ac6fb7b | ||
|
|
d9941e01cc | ||
|
|
6c9ce5bee2 | ||
|
|
4962a1ece3 | ||
|
|
cf04ddca38 | ||
|
|
439a05d8c3 | ||
|
|
197f18402e | ||
|
|
bdf49d137e | ||
|
|
6b59d6a47b | ||
|
|
ce246296b3 | ||
|
|
5f29073abf | ||
|
|
eaa1f16564 | ||
|
|
eba8eeda78 | ||
|
|
5c3f3fd3da | ||
|
|
15a882d2ed | ||
|
|
545c2d7206 | ||
|
|
bca4219dd0 | ||
|
|
a7d737a698 | ||
|
|
7ab228629e | ||
|
|
787839e8ec | ||
|
|
b518142236 | ||
|
|
e267ff93f3 | ||
|
|
c15957b43a | ||
|
|
d9aaf78530 | ||
|
|
7b0de4aa0e | ||
|
|
fc68c9269d | ||
|
|
8bd5df3ac1 | ||
|
|
98b9d47482 | ||
|
|
0835e093e2 | ||
|
|
9e512c5a98 | ||
|
|
bf74a4cc46 | ||
|
|
6c1e528518 | ||
|
|
2da246b9f7 | ||
|
|
291d986810 | ||
|
|
5f14543668 | ||
|
|
8d92128f6b | ||
|
|
2f537bafa5 | ||
|
|
0aaf688c2d | ||
|
|
4124a175e1 | ||
|
|
e87617593f | ||
|
|
27a065182c | ||
|
|
5ea84e17c0 | ||
|
|
129a7c056a | ||
|
|
f59f1e532e | ||
|
|
866e0e1670 | ||
|
|
fc0fa9a221 | ||
|
|
fd4045965e | ||
|
|
8432eaefa0 | ||
|
|
64b7811c28 | ||
|
|
a910b5c6df | ||
|
|
8956878448 | ||
|
|
94140e0897 | ||
|
|
8599c959fe | ||
|
|
c80adac077 | ||
|
|
829fdef219 | ||
|
|
b8f2f0d69e | ||
|
|
4a976c469c | ||
|
|
880d5c8a4a | ||
|
|
cbe03b6255 | ||
|
|
43d4667028 | ||
|
|
948dfe651d | ||
|
|
b7fa981899 | ||
|
|
98ef68bdfd | ||
|
|
730d94741c | ||
|
|
3307af43c5 | ||
|
|
8ee1bcf1fa | ||
|
|
e2ecf3d03d | ||
|
|
f49099b94d | ||
|
|
5bc3b7c368 | ||
|
|
593e5e4a25 | ||
|
|
ce93ee8bd0 | ||
|
|
225f2e67b7 | ||
|
|
a8299cec29 | ||
|
|
7d79cd57ff | ||
|
|
68ff8a0484 | ||
|
|
4bc9c52a12 | ||
|
|
ac2502141d | ||
|
|
66f158b89b | ||
|
|
ff7a1c21ce | ||
|
|
e47a9bd721 | ||
|
|
fb1266c98b | ||
|
|
f9f436a490 | ||
|
|
7c0a5698eb | ||
|
|
2a44e2d1b2 | ||
|
|
9780ff9642 | ||
|
|
e023524481 | ||
|
|
a95cfac078 | ||
|
|
2c99464b47 | ||
|
|
f8a1bd1253 | ||
|
|
cb642e7c84 | ||
|
|
7bb1afb8ea | ||
|
|
1c44db2992 | ||
|
|
c7208319d3 | ||
|
|
dad90f616d | ||
|
|
0b428e4482 | ||
|
|
a8513f8b37 | ||
|
|
08b5dccd12 | ||
|
|
b3e788d3f4 | ||
|
|
f14722a40f | ||
|
|
a8b67c66f2 | ||
|
|
3ec6705e34 | ||
|
|
3cc3dea915 | ||
|
|
b7f72aa2f4 | ||
|
|
480febf043 | ||
|
|
cc971c06f4 | ||
|
|
6410c4c8ef | ||
|
|
f104ef1282 | ||
|
|
f4ccf22e16 | ||
|
|
740f0f61d8 | ||
|
|
cae22ae3b6 | ||
|
|
95286cd8b8 | ||
|
|
12509fabc7 | ||
|
|
20d88a6dea | ||
|
|
f7eea070f0 | ||
|
|
758f8f7bd4 | ||
|
|
ef1c6e9295 | ||
|
|
67c6335efc | ||
|
|
c31a91230d | ||
|
|
2c114db2f1 | ||
|
|
1cedb323ad | ||
|
|
39b42814e0 | ||
|
|
21f52aeeba | ||
|
|
b55e093075 | ||
|
|
4386b80f2e | ||
|
|
005f592d54 | ||
|
|
d5fa5a96a8 | ||
|
|
d0b64a6fc4 | ||
|
|
26500425f6 | ||
|
|
4c25b000b5 | ||
|
|
75005ec06d | ||
|
|
e9e73f8d03 | ||
|
|
5afc025cc9 | ||
|
|
7b3a889c92 | ||
|
|
bc90f71d35 | ||
|
|
aeb3f08280 | ||
|
|
1e12afa80a | ||
|
|
96c2021cb1 | ||
|
|
e2d27cded7 | ||
|
|
c41fdfb4ef | ||
|
|
4b5c3ea7a7 | ||
|
|
58a9610acf | ||
|
|
5890e59dbc | ||
|
|
070c54dff4 | ||
|
|
47b29094c3 | ||
|
|
218ace2b02 | ||
|
|
f1e78306cb | ||
|
|
03f97e4cae | ||
|
|
c3cc9e8b88 | ||
|
|
7758c15e95 | ||
|
|
09897bed7f | ||
|
|
614ff883bc | ||
|
|
631be0774d | ||
|
|
759fdcb2f9 | ||
|
|
3bf83f7642 | ||
|
|
1cb15316b0 | ||
|
|
960f0ccfa1 | ||
|
|
3c07e86fd3 | ||
|
|
3d968fc620 | ||
|
|
cd2e4db8a5 | ||
|
|
aaadcef52f | ||
|
|
7520d39e8b | ||
|
|
752dce9284 | ||
|
|
39b8b0a41f | ||
|
|
52ead45f05 | ||
|
|
9f31b73cda | ||
|
|
eb2924054f | ||
|
|
0cee4498f1 | ||
|
|
1e04c474ab | ||
|
|
b36ed6d9f9 | ||
|
|
c7e67d008a | ||
|
|
4b8f512217 | ||
|
|
3778e63330 | ||
|
|
61b0163d58 | ||
|
|
513779d3b8 | ||
|
|
761037dcc6 | ||
|
|
5b64f5ad35 | ||
|
|
4822056db7 | ||
|
|
d811f6a9ed | ||
|
|
f8f3a1b4a1 | ||
|
|
fe782a6e95 | ||
|
|
925c431ff7 | ||
|
|
b822faa6cf | ||
|
|
e6e3bc7cd9 | ||
|
|
14c1d864d9 | ||
|
|
a4cfab7ee0 | ||
|
|
4f984b987c | ||
|
|
07aba86b37 | ||
|
|
72ee9bb56c | ||
|
|
db65151890 | ||
|
|
4a297554bc | ||
|
|
543ba6c114 | ||
|
|
22a9529012 | ||
|
|
fa7c421588 | ||
|
|
318e8da250 | ||
|
|
364e06261a | ||
|
|
d5940fabcd | ||
|
|
498644afaf | ||
|
|
a7107686d7 | ||
|
|
8f77d9df4a | ||
|
|
a31330608b | ||
|
|
51a7070e26 | ||
|
|
de399278fc | ||
|
|
061a5af544 | ||
|
|
e7cab32fae | ||
|
|
fd98863b1f | ||
|
|
69a160f187 | ||
|
|
3eaa407c6f | ||
|
|
b0959488d1 | ||
|
|
4a325a8592 | ||
|
|
48bd1294eb | ||
|
|
311ce60b16 | ||
|
|
d4e0fc76bb | ||
|
|
010b86fd34 | ||
|
|
2607d6e419 | ||
|
|
69d5356d76 | ||
|
|
9ed9c68fda | ||
|
|
f6f93b6336 | ||
|
|
defa0a20e0 | ||
|
|
060ba226e6 | ||
|
|
dcf4ee905b | ||
|
|
be792c085a | ||
|
|
5ed7bc6321 | ||
|
|
df58e82237 | ||
|
|
a1be3cf147 |
No files matched your search
@@ -25,6 +25,14 @@ API changes
|
||||
|
||||
::
|
||||
|
||||
--- mpv 0.6.0 is released ---
|
||||
1.5 - change in X11 and "--wid" behavior again. The previous change didn't
|
||||
work as expected, and now the behavior can be explicitly controlled
|
||||
with the "input-x11-keyboard" option. This is only a temporary
|
||||
measure until XEmbed is implemented and confirmed working.
|
||||
1.4 - subtle change in X11 and "--wid" behavior
|
||||
(this change was added to 0.5.2, and broke some things, see #1090)
|
||||
--- mpv 0.5.0 is released ---
|
||||
1.3 - add MPV_MAKE_VERSION()
|
||||
1.2 - remove "stream-time-pos" property (no replacement)
|
||||
1.1 - remap dvdnav:// to dvd://
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// Plays a video from the command line in a window provided by mpv.
|
||||
// You likely want to play the video in your own window instead,
|
||||
// but that's not quite ready yet.
|
||||
// You may need a basic Info.plist and MainMenu.xib to make this work.
|
||||
|
||||
#include "../../libmpv/client.h"
|
||||
#include "shared.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
{
|
||||
mpv_handle *mpv;
|
||||
dispatch_queue_t queue;
|
||||
}
|
||||
@end
|
||||
|
||||
static void wakeup(void *);
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
// Read filename
|
||||
NSArray *args = [NSProcessInfo processInfo].arguments;
|
||||
if (args.count < 2) {
|
||||
NSLog(@"Expected filename on command line");
|
||||
exit(1);
|
||||
}
|
||||
NSString *filename = args[1];
|
||||
|
||||
// Deal with MPV in the background.
|
||||
queue = dispatch_queue_create("mpv", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_async(queue, ^{
|
||||
|
||||
mpv = mpv_create();
|
||||
if (!mpv) {
|
||||
printf("failed creating context\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Maybe set some options here, like default key bindings.
|
||||
// NOTE: Interaction with the window seems to be broken for now.
|
||||
check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes"));
|
||||
|
||||
check_error(mpv_initialize(mpv));
|
||||
|
||||
// Register to be woken up whenever mpv generates new events.
|
||||
mpv_set_wakeup_callback(mpv, wakeup, (__bridge void *) self);
|
||||
|
||||
// Load the indicated file
|
||||
const char *cmd[] = {"loadfile", filename.UTF8String, NULL};
|
||||
check_error(mpv_command(mpv, cmd));
|
||||
});
|
||||
}
|
||||
|
||||
- (void) handleEvent:(mpv_event *)event
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_SHUTDOWN:
|
||||
// Clean up and shut down.
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSApplication sharedApplication] terminate:nil];
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("event: %s\n", mpv_event_name(event->event_id));
|
||||
}
|
||||
}
|
||||
|
||||
- (void) readEvents
|
||||
{
|
||||
dispatch_async(queue, ^{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
if (event->event_id == MPV_EVENT_NONE)
|
||||
break;
|
||||
[self handleEvent:event];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void wakeup(void *context) {
|
||||
AppDelegate *a = (__bridge AppDelegate *) context;
|
||||
[a readEvents];
|
||||
}
|
||||
|
||||
// Ostensibly, mpv's window would be hooked up to this.
|
||||
- (BOOL) windowShouldClose:(id)sender
|
||||
{
|
||||
[self shutdown];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) shutdown
|
||||
{
|
||||
if (mpv) {
|
||||
const char *args[] = {"quit", NULL};
|
||||
mpv_command(mpv, args);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
// Delete this if you already have a main.m.
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSApplication *app = [NSApplication new];
|
||||
AppDelegate *delegate = [AppDelegate new];
|
||||
app.delegate = delegate;
|
||||
[app run];
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -45,6 +45,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
mpv_container = new QWidget(this);
|
||||
setCentralWidget(mpv_container);
|
||||
mpv_container->setAttribute(Qt::WA_NativeWindow);
|
||||
// If you have a HWND, use: int64_t wid = (intptr_t)hwnd;
|
||||
int64_t wid = mpv_container->winId();
|
||||
mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
|
||||
|
||||
@@ -52,6 +53,10 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
// mpv as backend would implement its own key bindings.
|
||||
mpv_set_option_string(mpv, "input-default-bindings", "yes");
|
||||
|
||||
// Enable keyboard input on the X11 window. For the messy details, see
|
||||
// --input-x11-keyboard on the manpage.
|
||||
mpv_set_option_string(mpv, "input-x11-keyboard", "yes");
|
||||
|
||||
// Let us receive property change events with MPV_EVENT_PROPERTY_CHANGE if
|
||||
// this property changes.
|
||||
mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
@@ -84,6 +89,24 @@ void MainWindow::handle_mpv_event(mpv_event *event)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_VIDEO_RECONFIG: {
|
||||
// Retrieve the new video size.
|
||||
int64_t w, h;
|
||||
if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
|
||||
mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 &&
|
||||
w > 0 && h > 0)
|
||||
{
|
||||
// Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily
|
||||
// imply a resize, and you should check yourself if the video
|
||||
// dimensions really changed.
|
||||
// mpv itself will scale/letter box the video to the container size
|
||||
// if the video doesn't fit.
|
||||
std::stringstream ss;
|
||||
ss << "Reconfig: " << w << " " << h;
|
||||
statusBar()->showMessage(QString::fromStdString(ss.str()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
mpv_terminate_destroy(mpv);
|
||||
mpv = NULL;
|
||||
|
||||
@@ -21,6 +21,7 @@ int main(int argc, char *argv[])
|
||||
// Enable default key bindings, so the user can actually interact with
|
||||
// the player (and e.g. close the window).
|
||||
check_error(mpv_set_option_string(ctx, "input-default-bindings", "yes"));
|
||||
mpv_set_option_string(ctx, "input-x11-keyboard", "yes");
|
||||
int val = 1;
|
||||
check_error(mpv_set_option(ctx, "osc", MPV_FORMAT_FLAG, &val));
|
||||
|
||||
|
||||
@@ -26,6 +26,15 @@ created scripts to help ease the process. These are the two recommended methods:
|
||||
Note that MinGW environments included in Linux distributions are often broken,
|
||||
outdated and useless, and usually don't use MinGW-w64.
|
||||
|
||||
Additional dependencies
|
||||
-----------------------
|
||||
|
||||
You need a pthread wrapper. It must be interoperable with native Windows
|
||||
threads. pthreads-win32 or MinGW pthreads might work.
|
||||
|
||||
If you want ``--input-file=...`` to work, you need libwaio. It's available
|
||||
from: git://midipix.org/waio
|
||||
|
||||
Example with MXE
|
||||
----------------
|
||||
|
||||
|
||||
+20
-19
@@ -72,15 +72,17 @@ Available filters are:
|
||||
|
||||
``tospdif=<yes|no>``
|
||||
Output raw AC-3 stream if ``no``, output to S/PDIF for
|
||||
passthrough if ``yes`` (default).
|
||||
pass-through if ``yes`` (default).
|
||||
|
||||
``bitrate=<rate>``
|
||||
The bitrate use for the AC-3 stream. Set it to 384 to get 384 kbps.
|
||||
|
||||
The default is 640. Some receivers might not be able to handle this.
|
||||
|
||||
Valid values: 32, 40, 48, 56, 64, 80, 96, 112, 128,
|
||||
160, 192, 224, 256, 320, 384, 448, 512, 576, 640.
|
||||
|
||||
The special value ``default`` selects a default bitrate based on the
|
||||
The special value ``auto`` selects a default bitrate based on the
|
||||
input channel number:
|
||||
|
||||
:1ch: 96
|
||||
@@ -92,7 +94,7 @@ Available filters are:
|
||||
|
||||
``minchn=<n>``
|
||||
If the input channel number is less than ``<minchn>``, the filter will
|
||||
detach itself (default: 5).
|
||||
detach itself (default: 3).
|
||||
|
||||
``sweep[=speed]``
|
||||
Produces a sine sweep.
|
||||
@@ -101,7 +103,7 @@ Available filters are:
|
||||
Sine function delta, use very low values to hear the sweep.
|
||||
|
||||
``sinesuppress[=freq:decay]``
|
||||
Remove a sine at the specified frequency. Useful to get rid of the 50/60Hz
|
||||
Remove a sine at the specified frequency. Useful to get rid of the 50/60 Hz
|
||||
noise on low quality audio equipment. It only works on mono input.
|
||||
|
||||
``<freq>``
|
||||
@@ -182,7 +184,7 @@ Available filters are:
|
||||
|
||||
``mpv --af=equalizer=11:11:10:5:0:-12:0:5:12:12 media.avi``
|
||||
Would amplify the sound in the upper and lower frequency region
|
||||
while canceling it almost completely around 1kHz.
|
||||
while canceling it almost completely around 1 kHz.
|
||||
|
||||
``channels=nch[:routes]``
|
||||
Can be used for adding, removing, routing and copying audio channels. If
|
||||
@@ -263,9 +265,8 @@ Available filters are:
|
||||
Filter for internal use only. Converts between 24-bit and 32-bit sample
|
||||
formats.
|
||||
|
||||
``convertsignendian``
|
||||
Filter for internal use only. Converts between signed/unsigned formats
|
||||
and formats with different endian.
|
||||
``convertsign``
|
||||
Filter for internal use only. Converts between signed/unsigned formats.
|
||||
|
||||
``volume[=<volumedb>[:...]]``
|
||||
Implements software volume control. Use this filter with caution since it
|
||||
@@ -277,8 +278,8 @@ Available filters are:
|
||||
once for every audio stream.
|
||||
|
||||
``<volumedb>``
|
||||
Sets the desired gain in dB for all channels in the stream from -200dB
|
||||
to +60dB, where -200dB mutes the sound completely and +60dB equals a
|
||||
Sets the desired gain in dB for all channels in the stream from -200 dB
|
||||
to +60 dB, where -200 dB mutes the sound completely and +60 dB equals a
|
||||
gain of 1000 (default: 0).
|
||||
``replaygain-track``
|
||||
Adjust volume gain according to the track-gain replaygain value stored
|
||||
@@ -311,7 +312,7 @@ Available filters are:
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=volume=10.1 media.avi``
|
||||
Would amplify the sound by 10.1dB and hard-clip if the sound level
|
||||
Would amplify the sound by 10.1 dB and hard-clip if the sound level
|
||||
is too high.
|
||||
|
||||
``pan=n:[<matrix>]``
|
||||
@@ -368,8 +369,8 @@ Available filters are:
|
||||
to the subwoofer.
|
||||
|
||||
``<fc>``
|
||||
cutoff frequency in Hz for the low-pass filter (20Hz to 300Hz)
|
||||
(default: 60Hz) For the best result try setting the cutoff frequency
|
||||
cutoff frequency in Hz for the low-pass filter (20 Hz to 300 Hz)
|
||||
(default: 60 Hz) For the best result try setting the cutoff frequency
|
||||
as low as possible. This will improve the stereo or surround sound
|
||||
experience.
|
||||
``<ch>``
|
||||
@@ -381,7 +382,7 @@ Available filters are:
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=sub=100:4 --audio-channels=5 media.avi``
|
||||
Would add a subwoofer channel with a cutoff frequency of 100Hz to
|
||||
Would add a subwoofer channel with a cutoff frequency of 100 Hz to
|
||||
output channel 4.
|
||||
|
||||
``center``
|
||||
@@ -409,7 +410,7 @@ Available filters are:
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=surround=15 --audio-channels=4 media.avi``
|
||||
Would add surround sound decoding with 15ms delay for the sound to
|
||||
Would add surround sound decoding with 15 ms delay for the sound to
|
||||
the rear speakers.
|
||||
|
||||
``delay[=[ch1,ch2,...]]``
|
||||
@@ -437,8 +438,8 @@ Available filters are:
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=delay=[10.5,10.5,0,0,7,0] media.avi``
|
||||
Would delay front left and right by 10.5ms, the two rear channels
|
||||
and the subwoofer by 0ms and the center channel by 7ms.
|
||||
Would delay front left and right by 10.5 ms, the two rear channels
|
||||
and the subwoofer by 0 ms and the center channel by 7 ms.
|
||||
|
||||
``export=mmapped_file:nsamples]``
|
||||
Exports the incoming signal to other processes using memory mapping
|
||||
@@ -588,7 +589,7 @@ Available filters are:
|
||||
tempo.
|
||||
|
||||
``mpv --af=scaletempo=stride=30:overlap=.50:search=10 media.ogg``
|
||||
Would tweak the quality and performace parameters.
|
||||
Would tweak the quality and performance parameters.
|
||||
|
||||
``mpv --af=format=float,scaletempo media.ogg``
|
||||
Would make scaletempo use float code. Maybe faster on some
|
||||
@@ -600,7 +601,7 @@ Available filters are:
|
||||
1.2x.
|
||||
|
||||
``lavfi=graph``
|
||||
Filter audio using ffmpeg's libavfilter.
|
||||
Filter audio using FFmpeg's libavfilter.
|
||||
|
||||
``<graph>``
|
||||
Libavfilter graph. See ``lavfi`` video filter for details - the graph
|
||||
|
||||
+4
-1
@@ -23,7 +23,7 @@ normal driver parameters.
|
||||
where PulseAudio is used. On Windows, ``--ao=wasapi`` is preferred,
|
||||
though it might cause trouble sometimes, in which case ``--ao=dsound``
|
||||
should be used. On BSD systems, ``--ao=oss`` or `--ao=sndio`` may work
|
||||
(the latter being experimental). On OSX systems, use ``--ao=coreaudio``.
|
||||
(the latter being experimental). On OS X systems, use ``--ao=coreaudio``.
|
||||
|
||||
.. admonition:: Examples
|
||||
|
||||
@@ -181,6 +181,9 @@ Available audio output drivers are:
|
||||
Sets the device number to use. Playing a file with ``-v`` will show a
|
||||
list of available devices.
|
||||
|
||||
``buffersize=<ms>``
|
||||
DirectSound buffer size in milliseconds (default: 200).
|
||||
|
||||
``sdl``
|
||||
SDL 1.2+ audio output driver. Should work on any platform supported by SDL
|
||||
1.2, but may require the ``SDL_AUDIODRIVER`` environment variable to be set
|
||||
|
||||
+18
-16
@@ -19,14 +19,14 @@ General Changes for MPlayer to mpv
|
||||
Linux 2.4 (including VIDIX)
|
||||
* Large internal cleanups
|
||||
* Removal of support for dead platforms
|
||||
* New buildsystem
|
||||
* No embedded copy of ffmpeg and other libraries
|
||||
* New build system
|
||||
* No embedded copy of FFmpeg and other libraries
|
||||
* Better pause handling (do not unpause on a command)
|
||||
* Better MKV support (such as ordered chapters)
|
||||
* vo_vdpau improvements
|
||||
* Precise seeking support
|
||||
* Native OpenGL backend for OSX
|
||||
* General OSX improvements
|
||||
* Native OpenGL backend for OS X
|
||||
* General OS X improvements
|
||||
* Improvements in audio/video sync handling
|
||||
* Cleaned up terminal output
|
||||
* Gapless audio support (``--gapless-audio``)
|
||||
@@ -35,14 +35,14 @@ General Changes for MPlayer to mpv
|
||||
* OSS4 volume control
|
||||
* More correct color reproduction (color matrix generation)
|
||||
* Use libass for subtitle rendering by default (better quality)
|
||||
* Generally preferring ffmpeg/libav over internal demuxers and decoders
|
||||
* Generally preferring FFmpeg/Libav over internal demuxers and decoders
|
||||
* Improvements when playing multiple files (``--fixed-vo``)
|
||||
* Screenshot improvements (instant screenshots without 1-frame delay, allow
|
||||
taking screenshots even with hardware decoding)
|
||||
* Improved support for PulseAudio
|
||||
* Generally improved MS Windows support (dealing with unicode filenames,
|
||||
* Generally improved MS Windows support (dealing with Unicode file names,
|
||||
improved ``--vo=direct3d``, improved window handling)
|
||||
* Better OSD rendering (using libass). This has full unicode support, and
|
||||
* Better OSD rendering (using libass). This has full Unicode support, and
|
||||
languages like Arabic should be better supported.
|
||||
* Cleaned up terminal output (nicer status line, less useless noise)
|
||||
* Support for playing URLs of popular streaming sites directly
|
||||
@@ -50,19 +50,19 @@ General Changes for MPlayer to mpv
|
||||
* Improved OpenGL output (``--vo=opengl-hq``)
|
||||
* Make ``--softvol`` default (**mpv** is not a mixer control panel)
|
||||
* Improved support for .cue files
|
||||
* Screenshot improvements (can save screenshots as JPG or PNG, configurable
|
||||
filenames, support for taking screenshots with or without subtitles - the
|
||||
* Screenshot improvements (can save screenshots as JPEG or PNG, configurable
|
||||
file names, support for taking screenshots with or without subtitles - the
|
||||
``screenshot`` video filter is not needed anymore, and should not be put
|
||||
into the mpv config file)
|
||||
* Removal of teletext support
|
||||
* Removal of most builtin demuxers, using libavformat instead
|
||||
* Removal of builtin network support, using libavformat instead (also,
|
||||
* Removal of most built-in demuxers, using libavformat instead
|
||||
* Removal of built-in network support, using libavformat instead (also,
|
||||
support https via libavformat)
|
||||
* Replace image VOs (``--vo=jpeg`` etc.) with ``--vo=image``
|
||||
* Do not lose settings when playing a new file in the same player instance
|
||||
* New location for config files, new name for the binary.
|
||||
* Slave mode compatibility broken (see below)
|
||||
* Encoding functionality (replacement for mencoder, see ``DOCS/encoding.rst``)
|
||||
* Encoding functionality (replacement for MEncoder, see ``DOCS/encoding.rst``)
|
||||
* Remove ``--vo=gif89a``, ``--vo=md5sum``, ``--vo=yuv4mpeg``, as encoding can
|
||||
handle these use cases. For yuv4mpeg, for example, use:
|
||||
``mpv input.mkv -o output.y4m --no-audio --oautofps --oneverdrop``.
|
||||
@@ -71,10 +71,10 @@ General Changes for MPlayer to mpv
|
||||
* Wayland support
|
||||
* Support for precise scrolling which scales the parameter of commands. If the
|
||||
input doesn't support precise scrolling the scale factor stays 1.
|
||||
* OSX: Cocoa event loop is independent from mplayer's event loop, so user
|
||||
* OS X: Cocoa event loop is independent from MPlayer's event loop, so user
|
||||
actions like accessing menus and live resizing do not block the playback.
|
||||
* OSX: Apple Remote support.
|
||||
* OSX: Media Keys support.
|
||||
* OS X: Apple Remote support.
|
||||
* OS X: Media Keys support.
|
||||
* Windows: Added WASAPI audio output.
|
||||
* New OSD bar with chapter marks and not positioned in the middle of the video
|
||||
(though this can be customized with the ``--osd-bar-align-y`` option).
|
||||
@@ -99,7 +99,7 @@ General Changes for MPlayer to mpv
|
||||
* Allow changing/adjusting video filters at runtime. (This is also used to make
|
||||
the ``D`` key insert vf_yadif if deinterlacing is not supported otherwise.)
|
||||
* Native VAAPI support
|
||||
* OSX: VDA support using libavcodec hwaccel API insted of FFmpeg's decoder. Up
|
||||
* OS X: VDA support using libavcodec hwaccel API instead of FFmpeg's decoder. Up
|
||||
to 2-2.5x reduction in CPU usage.
|
||||
* Make hardware decoding in general work with the ``opengl`` video output.
|
||||
* Lua scripting (see `LUA SCRIPTING`_)
|
||||
@@ -332,6 +332,8 @@ Slave mode
|
||||
|
||||
Assuming the system supports ``/dev/stdin``.
|
||||
|
||||
(The option was readded in 0.5.1 and sets exactly these options.)
|
||||
|
||||
* A JSON RPC protocol giving access to the client API is planned, but nothing
|
||||
has emerged yet.
|
||||
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ You can encode files from one format/codec to another using this facility.
|
||||
|
||||
``--oautofps``
|
||||
Sets the output format time base to the guessed frame rate of the input
|
||||
video (simulates mencoder behavior, useful for AVI; may cause frame drops).
|
||||
video (simulates MEncoder behavior, useful for AVI; may cause frame drops).
|
||||
Note that not all codecs and not all formats support VFR encoding, and some
|
||||
which do have bugs when a target bitrate is specified - use ``--ofps`` or
|
||||
``--oautofps`` to force CFR encoding in these cases.
|
||||
@@ -74,7 +74,7 @@ You can encode files from one format/codec to another using this facility.
|
||||
.. admonition:: Example
|
||||
|
||||
"``--oac=libmp3lame --oacopts=b=128000``"
|
||||
selects 128kbps MP3 encoding.
|
||||
selects 128 kbps MP3 encoding.
|
||||
|
||||
Options are managed in lists. There are a few commands to manage the
|
||||
options list.
|
||||
|
||||
+78
-26
@@ -11,8 +11,9 @@ value (upper case if combined with ``Shift``), or a name for special keys. For
|
||||
example, ``a`` maps to the ``a`` key without shift, and ``A`` maps to ``a``
|
||||
with shift.
|
||||
|
||||
The file is normally located in ``~/.config/mpv/input.conf`` (some platforms
|
||||
may use different paths). The default bindings are defined here::
|
||||
The file is located in the mpv configuration directory (normally at
|
||||
``~/.config/mpv/input.conf`` depending on platform). The default bindings are
|
||||
defined here::
|
||||
|
||||
https://github.com/mpv-player/mpv/blob/master/etc/input.conf
|
||||
|
||||
@@ -114,7 +115,7 @@ List of Input Commands
|
||||
to be precise, not fast), and sometimes fails to behave as expected. How
|
||||
well this works depends on whether precise seeking works correctly (e.g.
|
||||
see the ``--hr-seek-demuxer-offset`` option). Video filters or other video
|
||||
postprocessing that modifies timing of frames (e.g. deinterlacing) should
|
||||
post-processing that modifies timing of frames (e.g. deinterlacing) should
|
||||
usually work, but might make backstepping silently behave incorrectly in
|
||||
corner cases. Using ``--hr-seek-framedrop=no`` should help, although it
|
||||
might make precise seeking slower.
|
||||
@@ -159,7 +160,9 @@ List of Input Commands
|
||||
Take a single screenshot.
|
||||
<each-frame>
|
||||
Take a screenshot each frame. Issue this command again to stop taking
|
||||
screenshots.
|
||||
screenshots. Note that you should disable frame-dropping when using
|
||||
this mode - or you might receive duplicate images in cases when a
|
||||
frame was dropped.
|
||||
|
||||
``screenshot_to_file "<filename>" [subtitles|video|window]``
|
||||
Take a screenshot and save it to a given file. The format of the file will
|
||||
@@ -258,7 +261,7 @@ List of Input Commands
|
||||
exactly as in the ``quit`` command.
|
||||
|
||||
``sub_add "<file>"``
|
||||
Load the given subtitle file. It is not selected as current subtitle after
|
||||
Load the given subtitle file. It is selected as current subtitle after
|
||||
loading.
|
||||
|
||||
``sub_remove [<id>]``
|
||||
@@ -266,7 +269,7 @@ List of Input Commands
|
||||
the current track. (Works on external subtitle files only.)
|
||||
|
||||
``sub_reload [<id>]``
|
||||
Reload the given subtitle tracks. If the ``id`` argument is missing, remove
|
||||
Reload the given subtitle tracks. If the ``id`` argument is missing, reload
|
||||
the current track. (Works on external subtitle files only.)
|
||||
|
||||
This works by unloading and re-adding the subtitle track.
|
||||
@@ -325,6 +328,12 @@ List of Input Commands
|
||||
Write the resume config file that the ``quit_watch_later`` command writes,
|
||||
but continue playback normally.
|
||||
|
||||
``stop``
|
||||
Stop playback and clear playlist. With default settings, this is
|
||||
essentially like ``quit``. Useful for the client API: playback can be
|
||||
stopped without terminating the player.
|
||||
|
||||
|
||||
Input Commands that are Possibly Subject to Change
|
||||
--------------------------------------------------
|
||||
|
||||
@@ -501,8 +510,8 @@ Input Commands that are Possibly Subject to Change
|
||||
be used in input.conf to reassign such bindings.)
|
||||
|
||||
|
||||
Undocumented commands: ``tv_last_channel`` (TV/DVB only), ``stop`` (questionable
|
||||
use), ``get_property`` (?), ``vo_cmdline`` (experimental).
|
||||
Undocumented commands: ``tv_last_channel`` (TV/DVB only), ``get_property`` (?),
|
||||
``vo_cmdline`` (experimental).
|
||||
|
||||
Input Command Prefixes
|
||||
----------------------
|
||||
@@ -572,7 +581,7 @@ Property list
|
||||
See ``--osd-level``.
|
||||
|
||||
``osd-scale`` (RW)
|
||||
OSD font size multiplicator, see ``--osd-scale``.
|
||||
OSD font size multiplier, see ``--osd-scale``.
|
||||
|
||||
``loop`` (RW)
|
||||
See ``--loop``.
|
||||
@@ -594,6 +603,18 @@ Property list
|
||||
``${stream-end}``. For ordered chapters and such, the
|
||||
size of the currently played segment is returned.)
|
||||
|
||||
``estimated-frame-count``
|
||||
Total number of frames in current file.
|
||||
|
||||
.. note:: This is only an estimate. (It's computed from two unreliable
|
||||
quantities: fps and stream length.)
|
||||
|
||||
``estimated-frame-number``
|
||||
Number of current frame in current stream.
|
||||
|
||||
.. note:: This is only an estimate. (It's computed from two unreliable
|
||||
quantities: fps and possibly rounded timestamps.)
|
||||
|
||||
``path``
|
||||
Full path of the currently played file.
|
||||
|
||||
@@ -635,8 +656,12 @@ Property list
|
||||
disabled.
|
||||
|
||||
``drop-frame-count``
|
||||
Frames dropped because they arrived to late. Unavailable if video
|
||||
is disabled
|
||||
Frames dropped because they arrived too late. Doesn't necessarily indicate
|
||||
actual frame-drops, just the number of times the decoder was asked to drop.
|
||||
Unavailable if video is disabled
|
||||
|
||||
``vo-drop-frame-count``
|
||||
Frames dropped by VO (when using ``--framedrop=vo``).
|
||||
|
||||
``percent-pos`` (RW)
|
||||
Position in current file (0-100). The advantage over using this instead of
|
||||
@@ -644,9 +669,6 @@ Property list
|
||||
estimating the playback position from the byte position, if the file
|
||||
duration is not known.
|
||||
|
||||
``ratio-pos`` (RW)
|
||||
Position in current file (0.0-1.0). higher precision that ``percent-pos``.
|
||||
|
||||
``time-pos`` (RW)
|
||||
Position in current file in seconds.
|
||||
|
||||
@@ -765,7 +787,7 @@ Property list
|
||||
::
|
||||
|
||||
MPV_FORMAT_NODE_MAP
|
||||
(key and string value for each metdata entry)
|
||||
(key and string value for each metadata entry)
|
||||
|
||||
``chapter-metadata``
|
||||
Metadata of current chapter. Works similar to ``metadata`` property. It
|
||||
@@ -809,7 +831,7 @@ Property list
|
||||
this easier, the cache resizing code will allocate the new cache while the
|
||||
old cache is still allocated.
|
||||
|
||||
Don't use this when playing DVD or Bluray.
|
||||
Don't use this when playing DVD or Blu-ray.
|
||||
|
||||
``cache-free`` (R)
|
||||
Total free cache size in KB.
|
||||
@@ -821,6 +843,15 @@ Property list
|
||||
Returns ``yes`` if the cache is idle, which means the cache is filled as
|
||||
much as possible, and is currently not reading more data.
|
||||
|
||||
``demuxer-cache-duration``
|
||||
Approximate duration of video buffered in the demuxer, in seconds. The
|
||||
guess is very unreliable, and often the property will not be available
|
||||
at all, even if data is buffered.
|
||||
|
||||
``demuxer-cache-idle``
|
||||
Returns ``yes`` if the demuxer is idle, which means the demuxer cache is
|
||||
filled to the requested amount, and is currently not reading more data.
|
||||
|
||||
``paused-for-cache``
|
||||
Returns ``yes`` when playback is paused because of waiting for the cache.
|
||||
|
||||
@@ -1007,13 +1038,17 @@ Property list
|
||||
``dwidth``, ``dheight``
|
||||
Video display size. This is the video size after filters and aspect scaling
|
||||
have been applied. The actual video window size can still be different
|
||||
from this.
|
||||
from this, e.g. if the user resized the video window manually.
|
||||
|
||||
These have the same values as ``video-out-params/dw`` and
|
||||
``video-out-params/dh``.
|
||||
|
||||
``video-out-params``
|
||||
Same as ``video-params``, but after video filters have been applied. If
|
||||
there are no video filters in use, this will contain the same values as
|
||||
``video-params``. Note that this is still not necessarily what the video
|
||||
window uses, since all real VOs do their own scaling.
|
||||
window uses, since the user can change the window size, and all real VOs
|
||||
do their own scaling independently from the filter chain.
|
||||
|
||||
Has the same sub-properties as ``video-params``.
|
||||
|
||||
@@ -1025,7 +1060,7 @@ Property list
|
||||
Estimated/measured FPS of the video filter chain output. (If no filters
|
||||
are used, this corresponds to decoder output.) This uses the average of
|
||||
the 10 past frame durations to calculate the FPS. It will be inaccurate
|
||||
if framedropping is involved (such as when framedrop is explicitly
|
||||
if frame-dropping is involved (such as when framedrop is explicitly
|
||||
enabled, or after precise seeking). Files with imprecise timestamps (such
|
||||
as Matroska) might lead to unstable results.
|
||||
|
||||
@@ -1033,7 +1068,7 @@ Property list
|
||||
Window size multiplier. Setting this will resize the video window to the
|
||||
values contained in ``dwidth`` and ``dheight`` multiplied with the value
|
||||
set with this property. Setting ``1`` will resize to original video size
|
||||
(or to be exactly, the size the video filters output). ``2`` will set the
|
||||
(or to be exact, the size the video filters output). ``2`` will set the
|
||||
double size, ``0.5`` halves the size.
|
||||
|
||||
``video-aspect`` (RW)
|
||||
@@ -1090,7 +1125,7 @@ Property list
|
||||
See ``--sub-forced-only``.
|
||||
|
||||
``sub-scale`` (RW)
|
||||
Subtitle font size multiplicator.
|
||||
Subtitle font size multiplier.
|
||||
|
||||
``ass-use-margins`` (RW)
|
||||
See ``--ass-use-margins``.
|
||||
@@ -1234,11 +1269,6 @@ Property list
|
||||
"title" MPV_FORMAT_STRING
|
||||
"time" MPV_FORMAT_DOUBLE
|
||||
|
||||
``quvi-format`` (RW)
|
||||
See ``--quvi-format``. Cycling this property (``cycle``) will attempt to
|
||||
cycle through known format, although currently this feature doesn't work
|
||||
well at all.
|
||||
|
||||
``af`` (RW)
|
||||
See ``--af`` and the ``af`` command.
|
||||
|
||||
@@ -1264,6 +1294,28 @@ Property list
|
||||
``seekable``
|
||||
Return whether it's generally possible to seek in the current file.
|
||||
|
||||
``osd-sym-cc``
|
||||
Inserts the current OSD symbol as opaque OSD control code (cc). This makes
|
||||
sense only with the ``show_text`` command or options which set OSD messages.
|
||||
The control code is implementation specific and is useless for anything else.
|
||||
|
||||
``osd-ass-cc``
|
||||
``${osd-ass-cc/0}`` disables escaping ASS sequences of text in OSD,
|
||||
``${osd-ass-cc/1}`` enables it again. By default, ASS sequences are
|
||||
escaped to avoid accidental formatting, and this property can disable
|
||||
this behavior. Note that the properties return an opaque OSD control
|
||||
code, which only makes sense for the ``show_text`` command or options
|
||||
which set OSD messages.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
--osd-status-msg='This is ${osd-ass-cc/0}{\\b1}bold text'
|
||||
|
||||
Any ASS override tags as understood by libass can be used.
|
||||
|
||||
Note that you need to escape the ``\`` character, because the string is
|
||||
processed for C escape sequences before passing it to the OSD code.
|
||||
|
||||
``options/<name>`` (RW)
|
||||
Read-only access to value of option ``--<name>``. Most options can be
|
||||
changed at runtime by writing to this property. Note that many options
|
||||
|
||||
+42
-18
@@ -1,10 +1,16 @@
|
||||
LUA SCRIPTING
|
||||
=============
|
||||
|
||||
mpv can load Lua scripts. Scripts in ``~/.mpv/lua/`` will be loaded on program
|
||||
start, or if passed to ``--lua``. mpv provides the builtin module ``mp``, which
|
||||
provides functions to send commands to the mpv core and to retrieve information
|
||||
about playback state, user settings, file information, and so on.
|
||||
mpv can load Lua scripts. Scripts passed to the ``--lua`` option, or found in
|
||||
the ``lua`` subdirectory of the mpv configuration directory (usually
|
||||
``~/.config/mpv/lua/``) will be loaded on program start. mpv also appends the
|
||||
``lua`` subdirectory to the end of Lua's path so you can import scripts from
|
||||
there too. Since it's added to the end, don't name scripts you want to import
|
||||
the same as Lua libraries because they will be overshadowed by them.
|
||||
|
||||
mpv provides the built-in module ``mp``, which provides functions to send
|
||||
commands to the mpv core and to retrieve information about playback state, user
|
||||
settings, file information, and so on.
|
||||
|
||||
These scripts can be used to control mpv in a similar way to slave mode.
|
||||
Technically, the Lua code uses the client API internally.
|
||||
@@ -27,12 +33,13 @@ allow users implement features which are not going to be added to the mpv core.
|
||||
Mode of operation
|
||||
-----------------
|
||||
|
||||
Your script will be loaded by the player at program start from ``~/.mpv/lua/``,
|
||||
or ``--lua``, or in some cases, internally (like ``--osc``). Each script runs
|
||||
in its own thread. Your script is first run "as is", and once that is done,
|
||||
the event loop is entered. This event loop will dispatch events received by mpv
|
||||
and call your own event handlers which you have registered with
|
||||
``mp.register_event``, or timers added with ``mp.add_timeout`` or similar.
|
||||
Your script will be loaded by the player at program start from the ``lua``
|
||||
configuration subdirectory, from a path specified with the ``--lua`` option, or
|
||||
in some cases, internally (like ``--osc``). Each script runs in its own
|
||||
thread. Your script is first run "as is", and once that is done, the event loop
|
||||
is entered. This event loop will dispatch events received by mpv and call your
|
||||
own event handlers which you have registered with ``mp.register_event``, or
|
||||
timers added with ``mp.add_timeout`` or similar.
|
||||
|
||||
When the player quits, all scripts will be asked to terminate. This happens via
|
||||
a ``shutdown`` event, which by default will make the event loop return. If your
|
||||
@@ -47,6 +54,15 @@ The event loop will wait for events and dispatch events registered with
|
||||
``mp.register_event``. It will also handle timers added with ``mp.add_timeout``
|
||||
and similar (by waiting with a timeout).
|
||||
|
||||
Since mpv 0.6.0, the player will wait until the script is fully loaded before
|
||||
continuing normal operation. The player considers a script as fully loaded as
|
||||
soon as it starts waiting for mpv events (or it exits). In practice this means
|
||||
the player will more or less hang until the script returns from the main chunk
|
||||
(and ``mp_event_loop`` is called), or the script calls ``mp_event_loop`` or
|
||||
``mp.dispatch_events`` directly. This is done to make it possible for a script
|
||||
to fully setup event handlers etc. before playback actually starts. In older
|
||||
mpv versions, this happened asynchronously.
|
||||
|
||||
mp functions
|
||||
------------
|
||||
|
||||
@@ -74,6 +90,10 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
These two commands are equivalent, except that the first version breaks
|
||||
if the filename contains spaces or certain special characters.
|
||||
|
||||
Note that properties are *not* expanded. You can use either ``mp.command``,
|
||||
the ``expand-properties`` prefix, or the ``mp.get_property`` family of
|
||||
functions.
|
||||
|
||||
``mp.get_property(name [,def])``
|
||||
Return the value of the given property as string. These are the same
|
||||
properties as used in input.conf. See `Properties`_ for a list of
|
||||
@@ -94,9 +114,9 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
will always result in a string.
|
||||
|
||||
``mp.get_property_bool(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value as boolean.
|
||||
Similar to ``mp.get_property``, but return the property value as Boolean.
|
||||
|
||||
Returns a boolean on success, or ``def, error`` on error.
|
||||
Returns a Boolean on success, or ``def, error`` on error.
|
||||
|
||||
``mp.get_property_number(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value as number.
|
||||
@@ -109,7 +129,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
|
||||
``mp.get_property_native(name [,def])``
|
||||
Similar to ``mp.get_property``, but return the property value using the best
|
||||
Lua type for the property. Most time, this will return a string, boolean,
|
||||
Lua type for the property. Most time, this will return a string, Boolean,
|
||||
or number. Some properties (for example ``chapter-list``) are returned as
|
||||
tables.
|
||||
|
||||
@@ -124,7 +144,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
|
||||
``mp.set_property_bool(name, value)``
|
||||
Similar to ``mp.set_property``, but set the given property to the given
|
||||
boolean value.
|
||||
Boolean value.
|
||||
|
||||
``mp.set_property_number(name, value)``
|
||||
Similar to ``mp.set_property``, but set the given property to the given
|
||||
@@ -159,7 +179,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
(e.g. ``ctrl+a``).
|
||||
|
||||
After calling this function, key presses will cause the function ``fn`` to
|
||||
be called (unless the user overmapped the key with another binding).
|
||||
be called (unless the user remapped the key with another binding).
|
||||
|
||||
The ``name`` argument should be a short symbolic string. It allows the user
|
||||
to remap the key binding via input.conf using the ``script_message``
|
||||
@@ -197,7 +217,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
|
||||
|
||||
This will print the message when the key ``y`` is pressed. (``x`` will
|
||||
still work, unless the user overmaps it.)
|
||||
still work, unless the user remaps it.)
|
||||
|
||||
You can also explicitly send a message to a named script only. Assume the
|
||||
above script was using the filename ``fooscript.lua``:
|
||||
@@ -311,7 +331,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
|
||||
``oneshot`` (RW)
|
||||
Whether the timer is periodic (``false``) or fires just once
|
||||
(``true``). This value is used when the timer expires (but before
|
||||
the timer calback function fn is run).
|
||||
the timer callback function fn is run).
|
||||
|
||||
|
||||
``mp.get_opt(key)``
|
||||
@@ -373,7 +393,7 @@ are useful only in special situations.
|
||||
control what the Lua script does (instead of being called by the default
|
||||
event loop), you can set the global variable ``mp_event_loop`` to your
|
||||
own function running the event loop. From your event loop, you should call
|
||||
``mp.dispatch_events()`` to unqueue and dispatch mpv events.
|
||||
``mp.dispatch_events()`` to dequeue and dispatch mpv events.
|
||||
|
||||
If the ``allow_wait`` parameter is set to ``true``, the function will block
|
||||
until the next event is received or the next timer expires. Otherwise (and
|
||||
@@ -482,6 +502,10 @@ This built-in module provides generic helper functions for Lua, and have
|
||||
strictly speaking nothing to do with mpv or video/audio playback. They are
|
||||
provided for convenience. Most compensate for Lua's scarce standard library.
|
||||
|
||||
``utils.getcwd()``
|
||||
Returns the directory that mpv was launched from. On error, ``nil, error``
|
||||
is returned.
|
||||
|
||||
``utils.readdir(path [, filter])``
|
||||
Enumerate all entries at the given path on the filesystem, and return them
|
||||
as array. Each entry is a directory entry (without the path).
|
||||
|
||||
+63
-8
@@ -73,7 +73,7 @@ p / SPACE
|
||||
Step backward. Pressing once will pause, every consecutive press will
|
||||
play one frame in reverse and then go into pause mode again.
|
||||
|
||||
q / ESC
|
||||
q
|
||||
Stop playing and quit.
|
||||
|
||||
Q
|
||||
@@ -98,6 +98,9 @@ m
|
||||
f
|
||||
Toggle fullscreen (see also ``--fs``).
|
||||
|
||||
ESC
|
||||
Exit fullscreen mode.
|
||||
|
||||
T
|
||||
Toggle stay-on-top (see also ``--ontop``).
|
||||
|
||||
@@ -123,6 +126,9 @@ j and J
|
||||
x and z
|
||||
Adjust subtitle delay by +/- 0.1 seconds.
|
||||
|
||||
Ctrl + and Ctrl -
|
||||
Adjust audio delay by +/- 0.1 seconds.
|
||||
|
||||
V
|
||||
Toggle subtitle VSFilter aspect compatibility mode. See
|
||||
``--ass-vsfilter-aspect-compat`` for more info.
|
||||
@@ -145,6 +151,10 @@ PGUP and PGDWN
|
||||
"previous" will actually go to the beginning of the current chapter; see
|
||||
``--chapter-seek-threshold``.
|
||||
|
||||
Shift+PGUP and Shift+PGDWN
|
||||
Seek backward or forward by 10 minutes. (This used to be mapped to
|
||||
PGUP/PGDWN without Shift.)
|
||||
|
||||
D
|
||||
Activate/deactivate deinterlacer.
|
||||
|
||||
@@ -163,7 +173,7 @@ corresponding adjustment, or the software equalizer (``--vf=eq``).)
|
||||
7 and 8
|
||||
Adjust saturation.
|
||||
|
||||
(The following keys are valid only on OSX.)
|
||||
(The following keys are valid only on OS X.)
|
||||
|
||||
command + 0
|
||||
Resize video window to half its original size.
|
||||
@@ -255,7 +265,7 @@ need to escape special characters. To work this around, the path can be
|
||||
additionally wrapped in the ``%n%string_of_length_n`` syntax (see above).
|
||||
|
||||
Some mpv options interpret paths starting with ``~``. Currently, the prefix
|
||||
``~~/`` expands to the mpv configuration directory (usually ``~/.mpv/``).
|
||||
``~~/`` expands to the mpv configuration directory (usually ``~/.config/mpv/``).
|
||||
``~/`` expands to the user's home directory. (The trailing ``/`` is always
|
||||
required.)
|
||||
|
||||
@@ -345,11 +355,11 @@ File-specific Configuration Files
|
||||
You can also write file-specific configuration files. If you wish to have a
|
||||
configuration file for a file called 'video.avi', create a file named
|
||||
'video.avi.conf' with the file-specific options in it and put it in
|
||||
``~/.mpv/``. You can also put the configuration file in the same directory
|
||||
``~/.config/mpv/``. You can also put the configuration file in the same directory
|
||||
as the file to be played, as long as you give the ``--use-filedir-conf``
|
||||
option (either on the command line or in your global config file). If a
|
||||
file-specific configuration file is found in the same directory, no
|
||||
file-specific configuration is loaded from ``~/.mpv``. In addition, the
|
||||
file-specific configuration is loaded from ``~/.config/mpv``. In addition, the
|
||||
``--use-filedir-conf`` option enables directory-specific configuration files.
|
||||
For this, mpv first tries to load a mpv.conf from the same directory
|
||||
as the file played and then tries to load any file-specific configuration.
|
||||
@@ -404,6 +414,51 @@ taking screenshots is handled by the VOs, and adding the screenshot filter will
|
||||
break hardware decoding. (The filter may still be useful for taking screenshots
|
||||
at a certain point within the video chain when using multiple video filters.)
|
||||
|
||||
TERMINAL STATUS LINE
|
||||
====================
|
||||
|
||||
During playback, mpv shows the playback status on the terminal. It looks like
|
||||
something like this:
|
||||
|
||||
``AV: 00:03:12 / 00:24:25 (13%) A-V: -0.000``
|
||||
|
||||
The status line can be overridden with the ``--term-status-msg`` option.
|
||||
|
||||
The following is a list of things that can show up in the status line. Input
|
||||
properties, that can be used to get the same information manually, are also
|
||||
listed.
|
||||
|
||||
- ``AV:`` or ``V:`` (video only) or ``A:`` (audio only)
|
||||
- The current time position in ``HH:MM:SS`` format (``playback-time`` property)
|
||||
- The total file duration (or ``00:00:00`` if unknown) (``length`` property)
|
||||
- Playback speed, e.g. `` x2.0``. Only visible if the speed is not normal. This
|
||||
is the user-requested speed, and not the actual speed (usually they should
|
||||
be the same, unless playback is too slow). (``speed`` property.)
|
||||
- Playback percentage, e.g. ``(13%)``. How much of the file has been played.
|
||||
Normally calculated out of playback position and duration, but can fallback
|
||||
to other methods (like byte position) if these are not available.
|
||||
(``percent-pos`` property.)
|
||||
- The audio/video sync as ``A-V: 0.000``. This is the difference between
|
||||
audio and video time. Normally it should be 0 or close to 0. If it's growing,
|
||||
it might indicate a playback problem. (``avsync`` property.)
|
||||
- Total A/V sync change, e.g. ``ct: -0.417``. Normally invisible. Can show up
|
||||
if there is audio "missing", or not enough frames can be dropped. Usually
|
||||
this will indicate a problem. (``total-avsync-change`` property.)
|
||||
- Encoding state in ``{...}``, only shown in encoding mode.
|
||||
- Decoder-dropped video frames, e.g. ``SD: 2``. Shows up only if the count is
|
||||
not 0. Normally should never show up, unless ``--framedrop`` is set to enable
|
||||
this mode, and your CPU is too slow. (``drop-frame-count`` property.)
|
||||
- VO-dropped video frames, e.g. ``D: 4``. Shows up only if the count is not 0.
|
||||
Can grow if the video framerate is higher than that of the display, or if
|
||||
video rendering is too slow. Also can be incremented on "hiccups" and when
|
||||
the video frame couldn't be displayed on time. (``vo-drop-frame-count``
|
||||
property.)
|
||||
- Cache state, e.g. ``Cache: 2s+134KB``. Visible if the stream cache is enabled.
|
||||
The first value shows the amount of video buffered in the demuxer in seconds,
|
||||
the second value shows *additional* data buffered in the stream cache in
|
||||
kilobytes. (``demuxer-cache-duration`` and ``cached-used`` properties.)
|
||||
|
||||
|
||||
PROTOCOLS
|
||||
=========
|
||||
|
||||
@@ -419,8 +474,8 @@ PROTOCOLS
|
||||
Play a path from Samba share.
|
||||
|
||||
``bd://[title][/device]`` ``--bluray-device=PATH``
|
||||
Play a Blu-Ray disc. Currently, this does not accept iso files. Instead,
|
||||
you must mount the iso file as filesystem, and point ``--bluray-device``
|
||||
Play a Blu-Ray disc. Currently, this does not accept ISO files. Instead,
|
||||
you must mount the ISO file as filesystem, and point ``--bluray-device``
|
||||
to the mounted directly.
|
||||
|
||||
``bdnav://[title][/device]``
|
||||
@@ -672,7 +727,7 @@ Play only chapters 5, 6, 7:
|
||||
Play only titles 5, 6, 7:
|
||||
``mpv dvd://5-7``
|
||||
|
||||
Play a multiangle DVD:
|
||||
Play a multi-angle DVD:
|
||||
``mpv dvd://1 --dvd-angle=2``
|
||||
|
||||
Play from a different DVD device:
|
||||
|
||||
+266
-147
@@ -198,26 +198,22 @@ Playback Control
|
||||
(i.e. not with stdin, pipe, etc).
|
||||
|
||||
``--load-unsafe-playlists``
|
||||
Normally, something like ``mpv playlist.m3u`` won't load the playlist. This
|
||||
is because the playlist code is unsafe. (This is the same in all other
|
||||
variations of MPlayer.)
|
||||
Load URLs from playlists which are considered unsafe (default: no). This
|
||||
includes special protocols and anything that doesn't refer to normal files.
|
||||
Local files and HTTP links on the other hand are always considered safe.
|
||||
|
||||
See ``--playlist`` for details.
|
||||
Note that ``--playlist`` always loads all entries, so you use that instead
|
||||
if you really have the need for this functionality.
|
||||
|
||||
Note: this option will allow opening playlists using the ``playlist``
|
||||
special demuxer. The ``--playlist`` uses different code, and supports more
|
||||
playlist formats than the playlist demuxer. This means that for now, the
|
||||
``--playlist`` option should always be used if you intend to open playlists.
|
||||
Background: the special demuxer contains newly written code, while the
|
||||
``--playlist`` option uses the old MPlayer code. Adding support for more
|
||||
playlist formats to the special demuxer is work in progress, and eventually
|
||||
the old code should disappear.
|
||||
``--loop-file=<N|inf|no>``
|
||||
Loop a single file N times. ``inf`` means forever, ``no`` means normal
|
||||
playback. For compatibility, ``--loop-file`` and ``--loop-file=yes`` are
|
||||
also accepted, and are the same as ``--loop-file=inf``.
|
||||
|
||||
``--loop-file``
|
||||
Loop a single file. The difference to ``--loop=inf`` is that this doesn't
|
||||
loop the playlist, just the file itself. If the playlist contains only a
|
||||
single file, the difference between the two option is that this option
|
||||
performs a seek on loop, instead of reloading the file.
|
||||
The difference to ``--loop`` is that this doesn't loop the playlist, just
|
||||
the file itself. If the playlist contains only a single file, the difference
|
||||
between the two option is that this option performs a seek on loop, instead
|
||||
of reloading the file.
|
||||
|
||||
``--ordered-chapters``, ``--no-ordered-chapters``
|
||||
Enabled by default.
|
||||
@@ -245,8 +241,8 @@ Playback Control
|
||||
Without ``--hr-seek``, skipping will snap to keyframes.
|
||||
|
||||
|
||||
Program Behaviour
|
||||
-----------------
|
||||
Program Behavior
|
||||
----------------
|
||||
|
||||
``--help``
|
||||
Show short summary of options.
|
||||
@@ -259,9 +255,8 @@ Program Behaviour
|
||||
Print version string and exit.
|
||||
|
||||
``--no-config``
|
||||
Do not load default configuration files. This prevents loading of
|
||||
``~/.config/mpv/mpv.conf`` and ``~/.config/mpv/input.conf``, as well as
|
||||
loading the same files from system wide configuration directories. Other
|
||||
Do not load default configuration files. This prevents loading of both the
|
||||
user-level and system-wide ``mpv.conf`` and ``input.conf`` files. Other
|
||||
configuration files are blocked as well, such as resume playback files.
|
||||
|
||||
.. note::
|
||||
@@ -317,7 +312,8 @@ Program Behaviour
|
||||
Specify configuration file to be parsed after the default ones.
|
||||
|
||||
``--load-scripts=<yes|no>``
|
||||
If set to ``no``, don't auto-load scripts from ``~/.mpv/lua/``.
|
||||
If set to ``no``, don't auto-load scripts from the ``lua`` configuration
|
||||
subdirectory (usually ``~/.config/mpv/lua/``).
|
||||
(Default: ``yes``)
|
||||
|
||||
``--lua=<filename>``
|
||||
@@ -332,14 +328,15 @@ Program Behaviour
|
||||
``--merge-files``
|
||||
Pretend that all files passed to mpv are concatenated into a single, big
|
||||
file. This uses timeline/EDL support internally. Note that this won't work
|
||||
for ordered chapter files or quvi-resolved URLs (such as youtube links).
|
||||
for ordered chapter files or quvi-resolved URLs (such as YouTube links).
|
||||
|
||||
This option is interpreted at program start, and doesn't affect for
|
||||
example files or playlists loaded with the ``loadfile`` or ``loadlist``
|
||||
commands.
|
||||
|
||||
``--no-resume-playback``
|
||||
Do not restore playback position from ``~/.mpv/watch_later/``.
|
||||
Do not restore playback position from the ``watch_later`` configuration
|
||||
subdirectory (usually ``~/.config/mpv/watch_later/``).
|
||||
See ``quit_watch_later`` input command.
|
||||
|
||||
``--profile=<profile1,profile2,...>``
|
||||
@@ -430,16 +427,45 @@ Video
|
||||
Do not sleep when outputting video frames. Useful for benchmarks when used
|
||||
with ``--no-audio.``
|
||||
|
||||
``--framedrop=<no|yes>``
|
||||
Skip displaying some frames to maintain A/V sync on slow systems. Video
|
||||
filters are not applied to such frames. For B-frames even decoding is
|
||||
skipped completely. May produce unwatchably choppy output.
|
||||
``--framedrop=<mode>``
|
||||
Skip displaying some frames to maintain A/V sync on slow systems, or
|
||||
playing high framerate video on video outputs that have an upper framerate
|
||||
limit.
|
||||
|
||||
The ``--vd-lavc-framedrop`` option controls what frames to drop.
|
||||
The argument selects the drop methods, and can be one of the following:
|
||||
|
||||
<no>
|
||||
Disable any framedropping.
|
||||
<vo>
|
||||
Drop late frames on video output (default). This still decodes and
|
||||
filters all frames, but doesn't render them on the VO. It tries to query
|
||||
the display FPS (X11 only, not correct on multi-monitor systems), or
|
||||
assumes infinite display FPS if that fails. Drops are indicated in
|
||||
the terminal status line as ``D:`` field. If the decoder is too slow,
|
||||
in theory all frames would have to be dropped (because all frames are
|
||||
too late) - to avoid this, frame dropping stops if the effective
|
||||
framerate is below 10 FPS.
|
||||
<decoder>
|
||||
Old, decoder-based framedrop mode. (This is the same as ``--framedrop=yes``
|
||||
in mpv 0.5.x and before.) This tells the decoder to skip frames (unless
|
||||
they are needed to decode future frames). May help with slow systems,
|
||||
but can produce unwatchably choppy output, or even freeze the display
|
||||
complete. Not recommended.
|
||||
The ``--vd-lavc-framedrop`` option controls what frames to drop.
|
||||
<decoder+vo>
|
||||
Enable both modes. Not recommended.
|
||||
|
||||
.. note::
|
||||
|
||||
Practical use of this feature is questionable. Disabled by default.
|
||||
``--vo=vdpau`` has its own code for the ``vo`` framedrop mode. Slight
|
||||
differences to other VOs are possible.
|
||||
|
||||
``--display-fps=<fps>``
|
||||
Set the maximum assumed display FPS used with ``--framedrop``. By default
|
||||
a detected value is used (X11 only, not correct on multi-monitor systems),
|
||||
or infinite display FPS if that fails. Infinite FPS means only frames too
|
||||
late are dropped. If a correct FPS is provided, frames that are predicted
|
||||
to be too late are dropped too.
|
||||
|
||||
``--hwdec=<api>``
|
||||
Specify the hardware video decoding API that should be used if possible.
|
||||
@@ -453,7 +479,7 @@ Video
|
||||
:vdpau: requires ``--vo=vdpau`` or ``--vo=opengl`` (Linux only)
|
||||
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only)
|
||||
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
|
||||
:vda: requires ``--vo=opengl`` or ``--vo=corevideo`` (OSX only)
|
||||
:vda: requires ``--vo=opengl`` (OS X only)
|
||||
|
||||
``auto`` tries to automatically enable hardware decoding using the first
|
||||
available method. This still depends what VO you are using. For example,
|
||||
@@ -531,6 +557,22 @@ Video
|
||||
which means the value ``0`` would rotate the video according to the
|
||||
rotation metadata.)
|
||||
|
||||
``--video-stereo-mode=<mode>``
|
||||
Set the stereo 3D output mode (default: ``mono``). This is done by inserting
|
||||
the ``stereo3d`` conversion filter.
|
||||
|
||||
The mode ``mono`` is an alias to ``ml``, which refers to the left frame in
|
||||
2D. This is the default, which means mpv will try to show 3D movies in 2D,
|
||||
instead of the mangled 3D image not intended for consumption (such as
|
||||
showing the left and right frame side by side, etc.).
|
||||
|
||||
The pseudo-mode ``none`` disables automatic conversion completely.
|
||||
|
||||
Use ``--video-stereo-mode=help`` to list all available modes. Check with
|
||||
the ``stereo3d`` filter documentation to see what the names mean. Note that
|
||||
some names refer to modes not supported by ``stereo3d`` - these modes can
|
||||
appear in files, but can't be handled properly by mpv.
|
||||
|
||||
``--video-zoom=<value>``
|
||||
Adjust the video display scale factor by the given value. The unit is in
|
||||
fractions of the (scaled) window video size.
|
||||
@@ -641,11 +683,6 @@ Video
|
||||
The following explanations are relevant:
|
||||
`<http://quvi.sourceforge.net/r/api/0.9/glossary_termino.html#m_stream_id>`_
|
||||
|
||||
The ``quvi-format`` property can be used at runtime to cycle through the
|
||||
list of formats. Unfortunately, this is slow. On libquvi 0.4.x, this
|
||||
functionality is limited to switching between ``best`` and ``default`` if
|
||||
the ``cycle`` input command is used.
|
||||
|
||||
``--vd-lavc-check-hw-profile=<yes|no>``
|
||||
Check hardware decoder profile (default: yes). If ``no`` is set, the
|
||||
highest profile of the hardware decoder is unconditionally selected, and
|
||||
@@ -750,7 +787,7 @@ Audio
|
||||
.. admonition:: Examples
|
||||
|
||||
``--ad=lavc:mp3float``
|
||||
Prefer the FFmpeg/Libav ``mp3float`` decoder over all other mp3
|
||||
Prefer the FFmpeg/Libav ``mp3float`` decoder over all other MP3
|
||||
decoders.
|
||||
|
||||
``--ad=spdif:ac3,lavc:*``
|
||||
@@ -821,12 +858,12 @@ Audio
|
||||
welcome. A full list of AVOptions can be found in the FFmpeg manual.
|
||||
|
||||
``--ad-spdif-dtshd=<yes|no>``, ``--dtshd``, ``--no-dtshd``
|
||||
When using DTS passthrough, output any DTS-HD track as-is.
|
||||
When using DTS pass-through, output any DTS-HD track as-is.
|
||||
With ``ad-spdif-dtshd=no`` (the default), only the DTS Core parts will be
|
||||
output.
|
||||
|
||||
DTS-HD tracks can be sent over HDMI but not over the original
|
||||
coax/toslink S/PDIF system.
|
||||
coax/TOSLINK S/PDIF system.
|
||||
|
||||
Some receivers don't accept DTS core-only when ``--ad-spdif-dtshd=yes`` is
|
||||
used, even though they accept DTS-HD.
|
||||
@@ -940,10 +977,6 @@ Audio
|
||||
maximum amplification, i.e. amplify by 200%. The default volume (no
|
||||
change in volume) will be ``50`` in this case.
|
||||
|
||||
``--volstep=<0-100>``
|
||||
Set the step size of mixer volume changes in percent of the full range
|
||||
(default: 3).
|
||||
|
||||
``--volume-restore-data=<string>``
|
||||
Used internally for use by playback resume (e.g. with ``quit_watch_later``).
|
||||
Restoring value has to be done carefully, because different AOs as well as
|
||||
@@ -956,6 +989,20 @@ Audio
|
||||
|
||||
Do not use.
|
||||
|
||||
``--audio-buffer=<seconds>``
|
||||
Set the audio output minimum buffer. The audio device might actually create
|
||||
a larger buffer if it pleases. If the device creates a smaller buffer,
|
||||
additional audio is buffered in an additional software buffer.
|
||||
|
||||
Making this larger will make soft-volume and other filters react slower,
|
||||
introduce additional issues on playback speed change, and block the
|
||||
player on audio format changes. A smaller buffer might lead to audio
|
||||
dropouts.
|
||||
|
||||
This option should be used for testing only. If a non-default value helps
|
||||
significantly, the mpv developers should be contacted.
|
||||
|
||||
Default: 0.2 (200 ms).
|
||||
|
||||
Subtitles
|
||||
---------
|
||||
@@ -1136,7 +1183,7 @@ Subtitles
|
||||
|
||||
``--ass-vsfilter-color-compat=<basic|full|force-601|no>``
|
||||
Mangle colors like (xy-)vsfilter do (default: basic). Historically, VSFilter
|
||||
was not colorspace aware. This was no problem as long as the colorspace
|
||||
was not color space aware. This was no problem as long as the color space
|
||||
used for SD video (BT.601) was used. But when everything switched to HD
|
||||
(BT.709), VSFilter was still converting RGB colors to BT.601, rendered
|
||||
them into the video frame, and handled the frame to the video output, which
|
||||
@@ -1146,16 +1193,16 @@ Subtitles
|
||||
|
||||
:basic: Handle only BT.601->BT.709 mangling, if the subtitles seem to
|
||||
indicate that this is required (default).
|
||||
:full: Handle the full ``YCbCr Matrix`` header with all video colorspaces
|
||||
:full: Handle the full ``YCbCr Matrix`` header with all video color spaces
|
||||
supported by libass and mpv. This might lead to bad breakages in
|
||||
corner cases and is not strictly needed for compatibility
|
||||
(hopefully), which is why this is not default.
|
||||
:force-601: Force BT.601->BT.709 mangling, regardless of subtitle headers
|
||||
or video colorspace.
|
||||
or video color space.
|
||||
:no: Disable color mangling completely. All colors are RGB.
|
||||
|
||||
Choosing anything other than ``no`` will make the subtitle color depend on
|
||||
the video colorspace, and it's for example in theory not possible to reuse
|
||||
the video color space, and it's for example in theory not possible to reuse
|
||||
a subtitle script with another video file. The ``--ass-style-override``
|
||||
option doesn't affect how this option is interpreted.
|
||||
|
||||
@@ -1177,7 +1224,7 @@ Subtitles
|
||||
Many studios tend to use bitmap fonts designed for square pixels when
|
||||
authoring DVDs, causing the fonts to look stretched on playback on DVD
|
||||
players. This option fixes them, however at the price of possibly
|
||||
misaligning sume subtitles (e.g. sign translations).
|
||||
misaligning some subtitles (e.g. sign translations).
|
||||
|
||||
Disabled by default.
|
||||
|
||||
@@ -1284,7 +1331,7 @@ Subtitles
|
||||
Also see ``--sub-speed`` option.
|
||||
|
||||
``--sub-gauss=<0.0-3.0>``
|
||||
Apply gaussian blur to image subtitles (default: 0). This can help making
|
||||
Apply Gaussian blur to image subtitles (default: 0). This can help making
|
||||
pixelated DVD/Vobsubs look nicer. A value other than 0 also switches to
|
||||
software subtitle scaling. Might be slow.
|
||||
|
||||
@@ -1316,12 +1363,19 @@ Subtitles
|
||||
- ``/path/to/video/sub/``
|
||||
- ``/path/to/video/subtitles/``
|
||||
- ``/tmp/subs/``
|
||||
- ``~/.mpv/sub/``
|
||||
- the ``sub`` configuration subdirectory (usually ``~/.config/mpv/sub/``)
|
||||
|
||||
``--sub-visibility``, ``--no-sub-visibility``
|
||||
Can be used to disable display of subtitles, but still select and decode
|
||||
them.
|
||||
|
||||
``--sub-clear-on-seek``
|
||||
(Obscure, rarely useful.) Can be used to play broken mkv files with
|
||||
duplicate ReadOrder fields. ReadOrder is the first field in a
|
||||
Matroska-style ASS subtitle packets. It should be unique, and libass
|
||||
uses it for fast elimination of duplicates. This option disables caching
|
||||
of subtitles across seeks, so after a seek libass can't eliminate subtitle
|
||||
packets with the same ReadOrder as earlier packets.
|
||||
|
||||
Window
|
||||
------
|
||||
@@ -1357,7 +1411,7 @@ Window
|
||||
``--fs-screen=<all|current|0-32>``
|
||||
In multi-monitor configurations (i.e. a single desktop that spans across
|
||||
multiple displays), this option tells mpv which screen to go fullscreen to.
|
||||
If ``default`` is provided mpv will fallback on using the behaviour
|
||||
If ``default`` is provided mpv will fallback on using the behavior
|
||||
depending on what the user provided with the ``screen`` option.
|
||||
|
||||
.. admonition:: Note (X11)
|
||||
@@ -1367,12 +1421,13 @@ Window
|
||||
|
||||
.. admonition:: Note (OS X)
|
||||
|
||||
``all`` does not work on OSX and will behave like ``current``.
|
||||
``all`` does not work on OS X and will behave like ``current``.
|
||||
|
||||
See also ``--screen``.
|
||||
|
||||
``--keep-open``
|
||||
Do not terminate when playing or seeking beyond the end of the file.
|
||||
Do not terminate when playing or seeking beyond the end of the file, and
|
||||
there is not next file to be played (and ``--loop`` is not used).
|
||||
Instead, pause the player. When trying to seek beyond end of the file, the
|
||||
player will pause at an arbitrary playback position (or, in corner cases,
|
||||
not redraw the window at all).
|
||||
@@ -1385,6 +1440,12 @@ Window
|
||||
chapter will terminate playback as well, even if ``--keep-open`` is
|
||||
given.
|
||||
|
||||
Since mpv 0.6.0, this doesn't pause if there is a next file in the playlist,
|
||||
or the playlist is looped. Approximately, this will pause when the player
|
||||
would normally exit, but in practice there are corner cases in which this
|
||||
is not the case (e.g. ``mpv --keep-open file.mkv /dev/null`` will play
|
||||
file.mkv normally, then fail to open ``/dev/null``, then exit).
|
||||
|
||||
``--force-window``
|
||||
Create a video output window even if there is no video. This can be useful
|
||||
when pretending that mpv is a GUI application. Currently, the window
|
||||
@@ -1429,7 +1490,7 @@ Window
|
||||
|
||||
.. admonition: Note (OS X)
|
||||
|
||||
On Mac OSX the origin of the screen coordinate system is located on the
|
||||
On Mac OS X the origin of the screen coordinate system is located on the
|
||||
bottom-left corner. For instance, ``0:0`` will place the window at the
|
||||
bottom-left of the screen.
|
||||
|
||||
@@ -1544,35 +1605,6 @@ Window
|
||||
depending on GPU drivers and hardware. For other VOs, this just makes
|
||||
rendering slower.
|
||||
|
||||
``--fs-missioncontrol``
|
||||
(OS X only)
|
||||
Use OS X Mission Control's fullscreen feature instead of the custom one
|
||||
provided by mpv. This can potentially break a lot of stuff like
|
||||
``--geometry`` and is disabled by default. On the other hand it provides
|
||||
a more 'OS X-like' user experience.
|
||||
|
||||
``--cursor-autohide=<number|no|always>``
|
||||
Make mouse cursor automatically hide after given number of milliseconds.
|
||||
``no`` will disable cursor autohide. ``always`` means the cursor will stay
|
||||
hidden.
|
||||
|
||||
``--cursor-autohide-fs-only``
|
||||
If this option is given, the cursor is always visible in windowed mode. In
|
||||
fullscreen mode, the cursor is shown or hidden according to
|
||||
``--cursor-autohide``.
|
||||
|
||||
``--no-fixed-vo``, ``--fixed-vo``
|
||||
``--no-fixed-vo`` enforces closing and reopening the video window for
|
||||
multiple files (one (un)initialization for each file).
|
||||
|
||||
``--force-rgba-osd-rendering``
|
||||
Change how some video outputs render the OSD and text subtitles. This
|
||||
does not change appearance of the subtitles and only has performance
|
||||
implications. For VOs which support native ASS rendering (like ``vdpau``,
|
||||
``opengl``, ``direct3d``), this can be slightly faster or slower,
|
||||
depending on GPU drivers and hardware. For other VOs, this just makes
|
||||
rendering slower.
|
||||
|
||||
``--force-window-position``
|
||||
Forcefully move mpv's video output window to default location whenever
|
||||
there is a change in video parameters, video stream or file. This used to
|
||||
@@ -1620,7 +1652,7 @@ Window
|
||||
.. note::
|
||||
|
||||
This does not affect the normal screensaver operation in any way.
|
||||
|
||||
|
||||
``--no-keepaspect``, ``--keepaspect``
|
||||
``--no-keepaspect`` will always stretch the video to window size, and will
|
||||
disable the window manager hints that force the window aspect ratio.
|
||||
@@ -1835,7 +1867,7 @@ Equalizer
|
||||
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 grey blacks and dim whites.
|
||||
the reverse in dim gray blacks and dim whites.
|
||||
|
||||
The same limitations as with ``--colormatrix`` apply.
|
||||
|
||||
@@ -1969,9 +2001,10 @@ Demuxer
|
||||
|
||||
``--demuxer-rawaudio-format=<value>``
|
||||
Sample format for ``--demuxer=rawaudio`` (default: s16le).
|
||||
Use ``--demuxer-rawaudio-format=help`` to get a list of all formats.
|
||||
|
||||
``--demuxer-rawaudio-rate=<value>``
|
||||
Sample rate for ``--demuxer=rawaudio`` (default: 44KHz).
|
||||
Sample rate for ``--demuxer=rawaudio`` (default: 44 kHz).
|
||||
|
||||
``--demuxer-rawvideo-fps=<value>``
|
||||
Rate in frames per second for ``--demuxer=rawvideo`` (default: 25.0).
|
||||
@@ -1987,11 +2020,11 @@ Demuxer
|
||||
--demuxer-rawvideo=w=720:h=576
|
||||
|
||||
``--demuxer-rawvideo-format=<value>``
|
||||
Colorspace (fourcc) in hex or string for ``--demuxer=rawvideo``
|
||||
Color space (fourcc) in hex or string for ``--demuxer=rawvideo``
|
||||
(default: ``YV12``).
|
||||
|
||||
``--demuxer-rawvideo-mp-format=<value>``
|
||||
Colorspace by internal video format for ``--demuxer=rawvideo``. Use
|
||||
Color space by internal video format for ``--demuxer=rawvideo``. Use
|
||||
``--demuxer-rawvideo-mp-format=help`` for a list of possible formats.
|
||||
|
||||
``--demuxer-rawvideo-codec=<value>``
|
||||
@@ -2004,13 +2037,25 @@ Demuxer
|
||||
|
||||
``--demuxer-thread=<yes|no>``
|
||||
Run the demuxer in a separate thread, and let it prefetch a certain amount
|
||||
of packets (default: no).
|
||||
of packets (default: yes). Having this enabled may lead to smoother
|
||||
playback, but on the other hand can add delays to seeking or track
|
||||
switching.
|
||||
|
||||
``--demuxer-readahead-secs=N``
|
||||
If ``--demuxer-thread`` is enabled, this controls how much the demuxer
|
||||
should buffer ahead in seconds (default: 0.2). As long as no packet has
|
||||
a timestamp difference higher than the readahead amount relative to the
|
||||
last packet returned to the decoder, the demuxer keeps reading.
|
||||
|
||||
(This value tends to be fuzzy, because many file formats don't store linear
|
||||
timestamps.)
|
||||
|
||||
``--demuxer-readahead-packets=N``
|
||||
If ``--demuxer-thread`` is enabled, this controls how much the demuxer
|
||||
should buffer ahead. If the number of packets in the packet queue exceeds
|
||||
``--demuxer-readahead-packets``, or the total number of bytes exceeds
|
||||
``--demuxer-readahead-bytes``, the thread stops reading ahead.
|
||||
should buffer ahead. As long as the number of packets in the packet queue
|
||||
doesn't exceed ``--demuxer-readahead-packets``, and the total number of
|
||||
bytes doesn't exceed ``--demuxer-readahead-bytes``, the thread keeps
|
||||
reading ahead.
|
||||
|
||||
Note that if you set these options near the maximum, you might get a
|
||||
packet queue overflow warning.
|
||||
@@ -2037,11 +2082,11 @@ Input
|
||||
Number of key presses to generate per second on autorepeat.
|
||||
|
||||
``--input-conf=<filename>``
|
||||
Specify input configuration file other than the default
|
||||
``~/.config/mpv/input.conf``.
|
||||
Specify input configuration file other than the default location in the mpv
|
||||
configuration directory (usually ``~/.config/mpv/input.conf``).
|
||||
|
||||
``--no-input-default-bindings``
|
||||
Disable mpv default (builtin) key bindings.
|
||||
Disable mpv default (built-in) key bindings.
|
||||
|
||||
``--input-cmdlist``
|
||||
Prints all commands that can be bound to keys.
|
||||
@@ -2107,13 +2152,31 @@ Input
|
||||
Specifies a configuration file for LIRC (default: ``~/.lircrc``).
|
||||
|
||||
``--input-media-keys``, ``--no-input-media-keys``
|
||||
OSX only: Enabled by default. Enables/disable media keys support.
|
||||
OS X only: Enabled by default. Enables/disable media keys support.
|
||||
|
||||
``--input-right-alt-gr``, ``--no-input-right-alt-gr``
|
||||
(Cocoa and Windows only)
|
||||
Use the right Alt key as Alt Gr to produce special characters. If disabled,
|
||||
count the right Alt as an Alt modifier key. Enabled by default.
|
||||
|
||||
``--input-x11-keyboard=<yes|no>``
|
||||
Disable all keyboard input on the X11 VO window. Generally useful for
|
||||
embedding only.
|
||||
|
||||
On X11, a sub-window with input enabled grabs all keyboard input as long
|
||||
as it is 1. a child of a focused window, and 2. the mouse is inside of
|
||||
the sub-window. The can steal away all keyboard input from the
|
||||
application embedding the mpv window, and on the other hand, the mpv
|
||||
window will receive no input if the mouse is outside of the mpv window,
|
||||
even though mpv has focus. Modern toolkits work around this weird X11
|
||||
behavior, but naively embedding foreign windows breaks it.
|
||||
|
||||
The only way to handle this reasonably is using the XEmbed protocol, which
|
||||
was designed to solve these problems. But Qt has questionable support, and
|
||||
mpv doesn't implement it yet.
|
||||
|
||||
As a workaround, this option is disabled by default in libmpv. (Note that
|
||||
``input-default-bindings`` is disabled by default in libmpv as well.)
|
||||
|
||||
OSD
|
||||
---
|
||||
@@ -2155,6 +2218,26 @@ OSD
|
||||
|
||||
Default: 45.
|
||||
|
||||
``--osd-msg1=<string>``
|
||||
Show this string as message on OSD with OSD level 1 (visible by default).
|
||||
The message will be visible by default, and as long no other message
|
||||
covers it, and the OSD level isn't changed (see ``--osd-level``).
|
||||
Expands properties; see `Property Expansion`_.
|
||||
|
||||
``--osd-msg2=<string>``
|
||||
Similar as ``--osd-msg1``, but for OSD level 2. If this is an empty string
|
||||
(default), then the playback time is shown.
|
||||
|
||||
``--osd-msg3=<string>``
|
||||
Similar as ``--osd-msg1``, but for OSD level 3. If this is an empty string
|
||||
(default), then the playback time, duration, and some more information is
|
||||
shown.
|
||||
|
||||
This is also used for the ``show_progress`` command (by default mapped to
|
||||
``P``), or in some non-default cases when seeking.
|
||||
|
||||
``--osd-status-msg`` is a legacy equivalent (but with a minor difference).
|
||||
|
||||
``--osd-status-msg=<string>``
|
||||
Show a custom string during playback instead of the standard status text.
|
||||
This overrides the status text used for ``--osd-level=3``, when using the
|
||||
@@ -2162,6 +2245,17 @@ OSD
|
||||
non-default cases when seeking. Expands properties. See
|
||||
`Property Expansion`_.
|
||||
|
||||
This option has been replaced with ``--osd-msg3``. The only difference is
|
||||
that this option implicitly includes ``${osd-sym-cc}``. This option is
|
||||
ignored if ``--osd-msg3`` is not empty.
|
||||
|
||||
``--osd-playing-msg=<string>``
|
||||
Show a message on OSD when playback starts. The string is expanded for
|
||||
properties, e.g. ``--osd-playing-msg='file: ${filename}'`` will show the
|
||||
message ``file:`` followed by a space and the currently played filename.
|
||||
|
||||
See `Property Expansion`_.
|
||||
|
||||
``--osd-bar-align-x=<-1-1>``
|
||||
Position of the OSD bar. -1 is far left, 0 is centered, 1 is far right.
|
||||
Fractional values (like 0.5) are allowed.
|
||||
@@ -2257,7 +2351,7 @@ OSD
|
||||
Default: 10.
|
||||
|
||||
``--osd-scale=<factor>``
|
||||
OSD font size multiplicator, multiplied with ``--osd-font-size`` value.
|
||||
OSD font size multiplier, multiplied with ``--osd-font-size`` value.
|
||||
|
||||
``--osd-scale-by-window=yes|no``
|
||||
Whether to scale the OSD with the window size (default: yes). If this is
|
||||
@@ -2301,7 +2395,7 @@ Screenshot
|
||||
``--screenshot-template=<template>``
|
||||
Specify the filename template used to save screenshots. The template
|
||||
specifies the filename without file extension, and can contain format
|
||||
specifiers, which will be substituted when taking a screeshot.
|
||||
specifiers, which will be substituted when taking a screenshot.
|
||||
By default the template is ``shot%n``, which results in filenames like
|
||||
``shot0012.png`` for example.
|
||||
|
||||
@@ -2414,10 +2508,10 @@ Software Scaler
|
||||
Default: ``bicubic``.
|
||||
|
||||
``--sws-lgb=<0-100>``
|
||||
Software scaler gaussian blur filter (luma). See ``--sws-scaler``.
|
||||
Software scaler Gaussian blur filter (luma). See ``--sws-scaler``.
|
||||
|
||||
``--sws-cgb=<0-100>``
|
||||
Software scaler gaussian blur filter (chroma). See ``--sws-scaler``.
|
||||
Software scaler Gaussian blur filter (chroma). See ``--sws-scaler``.
|
||||
|
||||
``--sws-ls=<-100-100>``
|
||||
Software scaler sharpen filter (luma). See ``--sws-scaler``.
|
||||
@@ -2496,7 +2590,7 @@ Terminal
|
||||
``--term-osd-bar-chars=<string>``
|
||||
Customize the ``--term-osd-bar`` feature. The string is expected to
|
||||
consist of 5 characters (start, left space, position indicator,
|
||||
right space, end). You can use unicode characters, but note that double-
|
||||
right space, end). You can use Unicode characters, but note that double-
|
||||
width characters will not be treated correctly.
|
||||
|
||||
Default: ``[-+-]``.
|
||||
@@ -2713,7 +2807,7 @@ Cache
|
||||
|
||||
May be useful when playing files from slow media, but can also have
|
||||
negative effects, especially with file formats that require a lot of
|
||||
seeking, such as mp4.
|
||||
seeking, such as MP4.
|
||||
|
||||
Note that half the cache size will be used to allow fast seeking back. This
|
||||
is also the reason why a full cache is usually reported as 50% full. The
|
||||
@@ -2726,20 +2820,6 @@ Cache
|
||||
will not automatically enable the cache e.g. when playing from a network
|
||||
stream. Note that using ``--cache`` will always override this option.
|
||||
|
||||
``--cache-pause-below=<kBytes|no>``
|
||||
If the cache size goes below the specified value (in KB), pause and wait
|
||||
until the size set by ``--cache-pause-restart`` is reached, then resume
|
||||
playback (default: 50). If ``no`` is specified, this behavior is disabled.
|
||||
|
||||
When the player is paused this way, the status line shows ``Buffering``
|
||||
instead of ``Paused``, and the OSD uses a clock symbol instead of the
|
||||
normal paused symbol.
|
||||
|
||||
``--cache-pause-restart=<kBytes>``
|
||||
If the cache is paused due to the ``--cache-pause-below`` functionality,
|
||||
then the player unpauses as soon as the cache has this much data (in KB).
|
||||
(Default: 100)
|
||||
|
||||
``--cache-initial=<kBytes>``
|
||||
Playback will start when the cache has been filled up with this many
|
||||
kilobytes of data (default: 0).
|
||||
@@ -2750,28 +2830,46 @@ Cache
|
||||
filled to this position rather than performing a stream seek (default:
|
||||
500).
|
||||
|
||||
This matters for small forward seeks. With slow streams (especially http
|
||||
This matters for small forward seeks. With slow streams (especially HTTP
|
||||
streams) there is a tradeoff between skipping the data between current
|
||||
position and seek destination, or performing an actual seek. Depending
|
||||
on the situation, either of these might be slower than the other method.
|
||||
This option allows control over this.
|
||||
|
||||
``--cache-file=<path>``
|
||||
Create a cache file on the filesystem with the given name. The file is
|
||||
always overwritten. When the general cache is enabled, this file cache
|
||||
will be used to store whatever is read from the source stream.
|
||||
``--cache-file=<TMP|path>``
|
||||
Create a cache file on the filesystem.
|
||||
|
||||
This will always overwrite the cache file, and you can't use an existing
|
||||
cache file to resume playback of a stream. (Technically, mpv wouldn't
|
||||
even know which blocks in the file are valid and which not.)
|
||||
There are two ways of using this:
|
||||
|
||||
The resulting file will not necessarily contain all data of the source
|
||||
stream. For example, if you seek, the parts that were skipped over are
|
||||
never read and consequently are not written to the cache. The skipped over
|
||||
parts are filled with zeros. This means that the cache file doesn't
|
||||
necessarily correspond to a full download of the source stream.
|
||||
1. Passing a path (a filename). The file will always be overwritten. When
|
||||
the general cache is enabled, this file cache will be used to store
|
||||
whatever is read from the source stream.
|
||||
|
||||
Both of these issues could be improved if there is any user interest.
|
||||
This will always overwrite the cache file, and you can't use an existing
|
||||
cache file to resume playback of a stream. (Technically, mpv wouldn't
|
||||
even know which blocks in the file are valid and which not.)
|
||||
|
||||
The resulting file will not necessarily contain all data of the source
|
||||
stream. For example, if you seek, the parts that were skipped over are
|
||||
never read and consequently are not written to the cache. The skipped over
|
||||
parts are filled with zeros. This means that the cache file doesn't
|
||||
necessarily correspond to a full download of the source stream.
|
||||
|
||||
Both of these issues could be improved if there is any user interest.
|
||||
|
||||
.. warning:: Causes random corruption when used with ordered chapters or
|
||||
with ``--audio-file``.
|
||||
|
||||
2. Passing the string ``TMP``. This will not be interpreted as filename.
|
||||
Instead, an invisible temporary file is created. It depends on your
|
||||
C library where this file is created (usually ``/tmp/``), and whether
|
||||
filename is visible (the ``tmpfile()`` function is used). On some
|
||||
systems, automatic deletion of the cache file might not be guaranteed.
|
||||
|
||||
If you want to use a file cache, this mode is recommended, because it
|
||||
doesn't break ordered chapters or ``--audio-file``. These modes open
|
||||
multiple cache streams, and using the same file for them obviously
|
||||
clashes.
|
||||
|
||||
Also see ``--cache-file-size``.
|
||||
|
||||
@@ -2779,11 +2877,24 @@ Cache
|
||||
Maximum size of the file created with ``--cache-file``. For read accesses
|
||||
above this size, the cache is simply not used.
|
||||
|
||||
Keep in mind that some use-cases, like playing ordered chapters with cache
|
||||
enabled, will actually create multiple cache files, each of which will
|
||||
use up to this much disk space.
|
||||
|
||||
(Default: 1048576, 1 GB.)
|
||||
|
||||
``--no-cache``
|
||||
Turn off input stream caching. See ``--cache``.
|
||||
|
||||
``--cache-secs=<seconds>``
|
||||
How many seconds of audio/video to prefetch if the cache is active. This
|
||||
overrides the ``--demuxer-readahead-secs`` option if and only if the cache
|
||||
is enabled. (Default: 2.)
|
||||
|
||||
``--cache-pause``, ``--no-cache-pause``
|
||||
Whether the player should automatically pause when the cache runs low,
|
||||
and unpause once more data is available ("buffering").
|
||||
|
||||
|
||||
Network
|
||||
-------
|
||||
@@ -2792,11 +2903,9 @@ Network
|
||||
Use ``<string>`` as user agent for HTTP streaming.
|
||||
|
||||
``--cookies``, ``--no-cookies``
|
||||
(network only)
|
||||
Support cookies when making HTTP requests. Disabled by default.
|
||||
|
||||
``--cookies-file=<filename>``
|
||||
(network only)
|
||||
Read HTTP cookies from <filename>. The file is assumed to be in Netscape
|
||||
format.
|
||||
|
||||
@@ -2822,11 +2931,11 @@ Network
|
||||
|
||||
``--tls-ca-file=<filename>``
|
||||
Certificate authority database file for use with TLS. (Silently fails with
|
||||
older ffmpeg or libav versions.)
|
||||
older FFmpeg or Libav versions.)
|
||||
|
||||
``--tls-verify``
|
||||
Verify peer certificates when using TLS (e.g. with ``https://...``).
|
||||
(Silently fails with older ffmpeg or libav versions.)
|
||||
(Silently fails with older FFmpeg or Libav versions.)
|
||||
|
||||
``--referrer=<string>``
|
||||
Specify a referrer path or URL for HTTP requests.
|
||||
@@ -2836,6 +2945,17 @@ Network
|
||||
network transport when playing ``rtsp://...`` URLs. The value ``lavf``
|
||||
leaves the decision to libavformat.
|
||||
|
||||
``--hls-bitrate=<no|min|max>``
|
||||
If HLS streams are played, this option controls what streams are selected
|
||||
by default. The option allows the following parameters:
|
||||
|
||||
:no: Don't do anything special. Typically, this will simply pick the
|
||||
first audio/video streams it can find. (Default.)
|
||||
:min: Pick the streams with the lowest bitrate.
|
||||
:max: Same, but highest bitrate.
|
||||
|
||||
The bitrate as used is sent by the server, and there's no guarantee it's
|
||||
actually meaningful.
|
||||
|
||||
DVB
|
||||
---
|
||||
@@ -2844,9 +2964,10 @@ DVB
|
||||
Specifies using card number 1-4 (default: 1).
|
||||
|
||||
``--dvbin-file=<filename>``
|
||||
Instructs mpv to read the channels list from ``<filename>``. Default is
|
||||
``~/.mpv/channels.conf.{sat,ter,cbl,atsc}`` (based on your card type) or
|
||||
``~/.mpv/channels.conf`` as a last resort.
|
||||
Instructs mpv to read the channels list from ``<filename>``. The default is
|
||||
in the mpv configuration directory (usually ``~/.config/mpv``) with the
|
||||
filename ``channels.conf.{sat,ter,cbl,atsc}`` (based on your card type) or
|
||||
``channels.conf`` as a last resort.
|
||||
|
||||
``--dvbin-timeout=<1-30>``
|
||||
Maximum number of seconds to wait when trying to tune a frequency before
|
||||
@@ -2894,8 +3015,8 @@ PVR
|
||||
``--pvr-vmode=<value>``
|
||||
Specify video encoding mode:
|
||||
|
||||
:vbr: Variable BitRate (default)
|
||||
:cbr: Constant BitRate
|
||||
:vbr: Variable Bit Rate (default)
|
||||
:cbr: Constant Bit Rate
|
||||
|
||||
``--pvr-vpeak=<value>``
|
||||
Specify peak video bitrate encoding in Mbps (only useful for VBR
|
||||
@@ -2970,6 +3091,11 @@ Miscellaneous
|
||||
You can also try to use ``--no-correct-pts`` for files with completely
|
||||
broken timestamps.
|
||||
|
||||
``--media-title=<string>``
|
||||
Force the contents of the ``media-title`` property to this value. Useful
|
||||
for scripts which want to set a title, without overriding the user's
|
||||
setting in ``--title``.
|
||||
|
||||
``--slave-broken``
|
||||
Switches on the old slave mode. This is for testing only, and incompatible
|
||||
to the removed ``--slave`` switch.
|
||||
@@ -2987,10 +3113,3 @@ Miscellaneous
|
||||
if there is enough interest).
|
||||
|
||||
This affects most third-party GUI frontends.
|
||||
|
||||
``--softsleep``
|
||||
Time frames by repeatedly checking the current time instead of asking
|
||||
the kernel to wake up mpv at the correct time. Useful if your kernel
|
||||
timing is imprecise and you cannot use the RTC either. Comes at the
|
||||
price of higher CPU consumption.
|
||||
|
||||
+5
-5
@@ -122,11 +122,11 @@ time remaining
|
||||
left-click toggle between total and remaining time
|
||||
============= ================================================
|
||||
|
||||
Keybindings
|
||||
~~~~~~~~~~~
|
||||
Key Bindings
|
||||
~~~~~~~~~~~~
|
||||
|
||||
These keybindings are active by default if nothing else is already bound to
|
||||
these keys. In case of collosision, the function needs to be bound to a
|
||||
These key bindings are active by default if nothing else is already bound to
|
||||
these keys. In case of collision, the function needs to be bound to a
|
||||
different key. See `Script Commands`_ section.
|
||||
|
||||
============= ================================================
|
||||
@@ -161,7 +161,7 @@ To avoid collisions with other scripts, all options need to be prefixed with
|
||||
|
||||
Example::
|
||||
|
||||
--lua-opts=osc-optionA=value1:osc-optionB=value2
|
||||
--lua-opts=osc-optionA=value1,osc-optionB=value2
|
||||
|
||||
|
||||
Configurable Options
|
||||
|
||||
+27
-21
@@ -93,7 +93,7 @@ Available filters are:
|
||||
|
||||
``scale[=w:h:param:param2:chr-drop:noup:arnd``
|
||||
Scales the image with the software scaler (slow) and performs a YUV<->RGB
|
||||
colorspace conversion (see also ``--sws``).
|
||||
color space conversion (see also ``--sws``).
|
||||
|
||||
All parameters are optional.
|
||||
|
||||
@@ -120,9 +120,9 @@ Available filters are:
|
||||
0.33:0.33 Mitchell-Netravali spline
|
||||
1.00:0.00 cubic B-spline
|
||||
|
||||
--sws=7 (gaussian): sharpness (0 (soft) - 100 (sharp))
|
||||
--sws=7 (Gaussian): sharpness (0 (soft) - 100 (sharp))
|
||||
|
||||
--sws=9 (lanczos): filter length (1-10)
|
||||
--sws=9 (Lanczos): filter length (1-10)
|
||||
|
||||
``<chr-drop>``
|
||||
chroma skipping
|
||||
@@ -222,8 +222,8 @@ Available filters are:
|
||||
====================== =====================
|
||||
|
||||
``noformat[=fmt]``
|
||||
Restricts the colorspace for the next filter without doing any conversion.
|
||||
Unlike the format filter, this will allow any colorspace except the one
|
||||
Restricts the color space for the next filter without doing any conversion.
|
||||
Unlike the format filter, this will allow any color space except the one
|
||||
you specify.
|
||||
|
||||
.. note:: For a list of available formats, see ``noformat=fmt=help``.
|
||||
@@ -231,12 +231,23 @@ Available filters are:
|
||||
``<fmt>``
|
||||
Format name, e.g. rgb15, bgr24, 420p, etc. (default: 420p).
|
||||
|
||||
``pp[=[filter1[:option1[:option2...]]/[-]filter2...]]``
|
||||
Enables the specified chain of postprocessing subfilters. Subfilters must
|
||||
``pp=[filter string]``
|
||||
Enables the specified chain of post-processing subfilters. Subfilters must
|
||||
be separated by '/' and can be disabled by prepending a '-'. Each
|
||||
subfilter and some options have a short and a long name that can be used
|
||||
interchangeably, i.e. ``dr``/``dering`` are the same. All subfilters share
|
||||
common options to determine their scope:
|
||||
interchangeably, i.e. ``dr``/``dering`` are the same.
|
||||
|
||||
.. note::
|
||||
|
||||
Unlike in MPlayer or in earlier versions, you must quote the pp string
|
||||
if it contains ``:`` characters, e.g. ``'--vf=pp=[...]'``.
|
||||
|
||||
.. admonition:: Warning
|
||||
|
||||
This filter is most likely useless on modern HD video. It might be
|
||||
helpful with old low-resolution files.
|
||||
|
||||
All subfilters share common options to determine their scope:
|
||||
|
||||
``a/autoq``
|
||||
Automatically switch the subfilter off if the CPU is too slow.
|
||||
@@ -251,11 +262,6 @@ Available filters are:
|
||||
|
||||
``--vf=pp:help`` shows a list of available subfilters.
|
||||
|
||||
.. note::
|
||||
|
||||
Unlike in MPlayer or in earlier versions, you must quote the pp string
|
||||
if it contains ``:`` characters, e.g. ``'--vf=pp=[...]'``.
|
||||
|
||||
Available subfilters are:
|
||||
|
||||
``hb/hdeblock[:difference[:flatness]]``
|
||||
@@ -336,7 +342,7 @@ Available filters are:
|
||||
filtering every second line with a (-1 4 2 4 -1) filter.
|
||||
|
||||
``l5/lowpass5``
|
||||
Vertically applied FIR lowpass deinterlacing filter that deinterlaces
|
||||
Vertically applied FIR low-pass deinterlacing filter that deinterlaces
|
||||
the given block by filtering all lines with a (-1 2 6 2 -1) filter.
|
||||
|
||||
``fq/forceQuant[:quantizer]``
|
||||
@@ -356,7 +362,7 @@ Available filters are:
|
||||
|
||||
.. note::
|
||||
|
||||
This filter is only available if FFmpeg/libav has been compiled
|
||||
This filter is only available if FFmpeg/Libav has been compiled
|
||||
with libpostproc enabled.
|
||||
|
||||
.. admonition:: Examples
|
||||
@@ -443,7 +449,7 @@ Available filters are:
|
||||
temporal noise (noise pattern changes between frames)
|
||||
|
||||
``uniform``
|
||||
uniform noise (gaussian otherwise)
|
||||
uniform noise (Gaussian otherwise)
|
||||
|
||||
``hq``
|
||||
high quality (slightly better looking, slightly slower) - not available
|
||||
@@ -503,7 +509,7 @@ Available filters are:
|
||||
:1: linear interpolation (default)
|
||||
|
||||
``unsharp[=lx:ly:la:cx:cy:ca]``
|
||||
unsharp mask / gaussian blur
|
||||
unsharp mask / Gaussian blur
|
||||
|
||||
``l`` is for the luma component, ``c`` for the chroma component. ``x``/``y``
|
||||
is the filter size. ``a`` is the amount.
|
||||
@@ -769,8 +775,8 @@ Available filters are:
|
||||
anaglyph red/cyan color (red filter on left eye, cyan filter on
|
||||
right eye)
|
||||
``arcd`` or ``anaglyph_red_cyan_dubois``
|
||||
anaglyph red/cyan color optimized with the least squares
|
||||
projection of dubois (red filter on left eye, cyan filter on right
|
||||
anaglyph red/cyan color optimized with the least-squares
|
||||
projection of Dubois (red filter on left eye, cyan filter on right
|
||||
eye)
|
||||
``agmg`` or ``anaglyph_green_magenta_gray``
|
||||
anaglyph green/magenta gray (green filter on left eye, magenta
|
||||
@@ -803,7 +809,7 @@ Available filters are:
|
||||
|
||||
``gradfun[=strength[:radius|:size=<size>]]``
|
||||
Fix the banding artifacts that are sometimes introduced into nearly flat
|
||||
regions by truncation to 8bit color depth. Interpolates the gradients that
|
||||
regions by truncation to 8-bit color depth. Interpolates the gradients that
|
||||
should go where the bands are, and dithers them.
|
||||
|
||||
``<strength>``
|
||||
|
||||
+60
-32
@@ -38,36 +38,36 @@ Available video output drivers are:
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``adaptor=<number>``
|
||||
Select a specific XVideo adaptor (check xvinfo results).
|
||||
Select a specific XVideo adapter (check xvinfo results).
|
||||
``port=<number>``
|
||||
Select a specific XVideo port.
|
||||
``ck=<cur|use|set>``
|
||||
Select the source from which the colorkey is taken (default: cur).
|
||||
Select the source from which the color key is taken (default: cur).
|
||||
|
||||
cur
|
||||
The default takes the colorkey currently set in Xv.
|
||||
The default takes the color key currently set in Xv.
|
||||
use
|
||||
Use but do not set the colorkey from mpv (use the ``--colorkey``
|
||||
Use but do not set the color key from mpv (use the ``--colorkey``
|
||||
option to change it).
|
||||
set
|
||||
Same as use but also sets the supplied colorkey.
|
||||
Same as use but also sets the supplied color key.
|
||||
|
||||
``ck-method=<man|bg|auto>``
|
||||
Sets the colorkey drawing method (default: man).
|
||||
Sets the color key drawing method (default: man).
|
||||
|
||||
man
|
||||
Draw the colorkey manually (reduces flicker in some cases).
|
||||
Draw the color key manually (reduces flicker in some cases).
|
||||
bg
|
||||
Set the colorkey as window background.
|
||||
Set the color key as window background.
|
||||
auto
|
||||
Let Xv draw the colorkey.
|
||||
Let Xv draw the color key.
|
||||
|
||||
``colorkey=<number>``
|
||||
Changes the colorkey to an RGB value of your choice. ``0x000000`` is
|
||||
Changes the color key to an RGB value of your choice. ``0x000000`` is
|
||||
black and ``0xffffff`` is white.
|
||||
|
||||
``no-colorkey``
|
||||
Disables colorkeying.
|
||||
Disables color-keying.
|
||||
|
||||
``x11`` (X11 only)
|
||||
Shared memory video output driver without hardware acceleration that works
|
||||
@@ -82,7 +82,7 @@ Available video output drivers are:
|
||||
.. note::
|
||||
|
||||
Earlier versions of mpv (and MPlayer, mplayer2) provided sub-options
|
||||
to tune vdpau postprocessing, like ``deint``, ``sharpen``, ``denoise``,
|
||||
to tune vdpau post-processing, like ``deint``, ``sharpen``, ``denoise``,
|
||||
``chroma-deint``, ``pullup``, ``hqscaling``. These sub-options are
|
||||
deprecated, and you should use the ``vdpaupp`` video filter instead.
|
||||
|
||||
@@ -173,7 +173,7 @@ Available video output drivers are:
|
||||
to a YUV format before the VO. Sometimes useful to force availability
|
||||
of certain YUV-only features, like video equalizer or deinterlacing.
|
||||
|
||||
Using the VDPAU frame queueing functionality controlled by the queuetime
|
||||
Using the VDPAU frame queuing functionality controlled by the queuetime
|
||||
options makes mpv's frame flip timing less sensitive to system CPU load and
|
||||
allows mpv to start decoding the next frame(s) slightly earlier, which can
|
||||
reduce jitter caused by individual slow-to-decode frames. However, the
|
||||
@@ -259,13 +259,6 @@ Available video output drivers are:
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``corevideo`` (Mac OS X 10.6 and later)
|
||||
Mac OS X CoreVideo video output driver. Uses the CoreVideo APIs to fill
|
||||
PixelBuffers and generate OpenGL textures from them (useful as a fallback
|
||||
for ``opengl``).
|
||||
|
||||
.. note:: This driver is for compatibility with old systems.
|
||||
|
||||
``opengl``
|
||||
OpenGL video output driver. It supports extended scaling methods, dithering
|
||||
and color management.
|
||||
@@ -282,7 +275,7 @@ Available video output drivers are:
|
||||
|
||||
Hardware decoding over OpenGL-interop is supported to some degree. Note
|
||||
that in this mode, some corner case might not be gracefully handled, and
|
||||
colorspace conversion and chroma upsampling is generally in the hand of
|
||||
color space conversion and chroma upsampling is generally in the hand of
|
||||
the hardware decoder APIs.
|
||||
|
||||
``lscale=<filter>``
|
||||
@@ -297,6 +290,9 @@ Available video output drivers are:
|
||||
``lanczos3``
|
||||
Lanczos with radius=3.
|
||||
|
||||
``lanczos``
|
||||
Generic Lanczos scaling filter. Set radius with ``lradius``.
|
||||
|
||||
``spline36``
|
||||
This is the default when using ``opengl-hq``.
|
||||
|
||||
@@ -316,6 +312,14 @@ Available video output drivers are:
|
||||
Mitchell-Netravali. The ``b`` and ``c`` parameters can be set with
|
||||
``lparam1`` and ``lparam2``. Both are set to 1/3 by default.
|
||||
|
||||
``gaussian``
|
||||
Gaussian filter with a parameter ``p`` for sharpness control.
|
||||
``p`` can be set to float number between 1(blurry) and 100(sharp)
|
||||
and has a default value of about 28.8 (see ``lparam1``).
|
||||
|
||||
Note that for extremely small value of ``p``, a large filter radius
|
||||
might be required to avoid unintended artifacts (see ``lradius``).
|
||||
|
||||
|
||||
There are some more filters. For a complete list, pass ``help`` as
|
||||
value, e.g.::
|
||||
@@ -329,6 +333,16 @@ Available video output drivers are:
|
||||
``lparam2=<value>``
|
||||
See ``lparam1``.
|
||||
|
||||
``lradius=<r>``
|
||||
Set radius for filters listed below, must be a float number between 1.0
|
||||
and 8.0. Defaults to be 2.0 if not specified.
|
||||
|
||||
``sinc``, ``lanczos``, ``blackman``, ``gaussian``
|
||||
|
||||
Note that depending on filter implementation details and video scaling
|
||||
ratio, the radius that actually being used might be different
|
||||
(most likely being increased a bit).
|
||||
|
||||
``scaler-resizes-only``
|
||||
Disable the scaler if the video image is not resized. In that case,
|
||||
``bilinear`` is used instead whatever is set with ``lscale``. Bilinear
|
||||
@@ -349,7 +363,7 @@ Available video output drivers are:
|
||||
green-magenta
|
||||
Convert side by side input to full-color green-magenta stereo.
|
||||
quadbuffer
|
||||
Convert side by side input to quadbuffered stereo. Only supported
|
||||
Convert side by side input to quad buffered stereo. Only supported
|
||||
by very few OpenGL cards.
|
||||
|
||||
``srgb``
|
||||
@@ -430,6 +444,11 @@ Available video output drivers are:
|
||||
RGB. If chroma is not subsampled, this option is ignored, and the
|
||||
luma scaler is used instead. Setting this option is often useless.
|
||||
|
||||
``cparam1``, ``cparam2``, ``cradius``
|
||||
Set filter parameters and radius for ``cscale``.
|
||||
|
||||
See ``lparam1``, ``lparam2`` and ``lradius``.
|
||||
|
||||
``fancy-downscaling``
|
||||
When using convolution based filters, extend the filter size
|
||||
when downscaling. Trades quality for reduced downscaling performance.
|
||||
@@ -439,7 +458,16 @@ Available video output drivers are:
|
||||
Borders will be distorted due to filtering.
|
||||
|
||||
``glfinish``
|
||||
Call ``glFinish()`` before swapping buffers
|
||||
Call ``glFinish()`` before and after swapping buffers (default: disabled).
|
||||
Slower, but might help getting better results when doing framedropping.
|
||||
The details depend entirely on the OpenGL driver.
|
||||
|
||||
``waitvsync``
|
||||
Call ``glXWaitVideoSyncSGI`` after each buffer swap (default: disabled).
|
||||
This may or may not help with video timing accuracy and frame drop. It's
|
||||
possible that this makes video output slower, or has no effect at all.
|
||||
|
||||
X11 only.
|
||||
|
||||
``sw``
|
||||
Continue even if a software renderer is detected.
|
||||
@@ -452,7 +480,7 @@ Available video output drivers are:
|
||||
auto
|
||||
auto-select (default)
|
||||
cocoa
|
||||
Cocoa/OSX
|
||||
Cocoa/OS X
|
||||
win
|
||||
Win32/WGL
|
||||
x11
|
||||
@@ -479,7 +507,7 @@ Available video output drivers are:
|
||||
|
||||
``gamma=<0.0..10.0>``
|
||||
Set a gamma value. If gamma is adjusted in other ways (like with
|
||||
the ``--gamma`` option or keybindings and the ``gamma`` property), the
|
||||
the ``--gamma`` option or key bindings and the ``gamma`` property), the
|
||||
value is multiplied with the other gamma value.
|
||||
|
||||
Setting this value to 1.0 can be used to always enable gamma control.
|
||||
@@ -487,7 +515,7 @@ Available video output drivers are:
|
||||
|
||||
``icc-profile=<file>``
|
||||
Load an ICC profile and use it to transform linear RGB to screen output.
|
||||
Needs LittleCMS2 support compiled in. This option overrides the ``srgb``
|
||||
Needs LittleCMS 2 support compiled in. This option overrides the ``srgb``
|
||||
property, as using both is somewhat redundant. It also enables linear
|
||||
light scaling.
|
||||
|
||||
@@ -500,12 +528,12 @@ Available video output drivers are:
|
||||
|
||||
``icc-cache=<file>``
|
||||
Store and load the 3D LUT created from the ICC profile in this file.
|
||||
This can be used to speed up loading, since LittleCMS2 can take a while
|
||||
This can be used to speed up loading, since LittleCMS 2 can take a while
|
||||
to create the 3D LUT. Note that this file contains an uncompressed LUT.
|
||||
Its size depends on the ``3dlut-size``, and can be very big.
|
||||
|
||||
``icc-intent=<value>``
|
||||
Specifies the ICC Intent used for transformations between colorspaces.
|
||||
Specifies the ICC Intent used for transformations between color spaces.
|
||||
This affects the rendering when using ``icc-profile`` or ``srgb`` and
|
||||
also affects the way DCP XYZ content gets converted to RGB.
|
||||
|
||||
@@ -562,7 +590,7 @@ Available video output drivers are:
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
--vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgb16
|
||||
--vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16
|
||||
|
||||
Note that some cheaper LCDs do dithering that gravely interferes with
|
||||
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
|
||||
@@ -571,7 +599,7 @@ Available video output drivers are:
|
||||
can achieve better quality or performance by changing the ``fbo-format``
|
||||
suboption to ``rgb16f``, ``rgb32f`` or ``rgb``. Known problems include
|
||||
Mesa/Intel not accepting ``rgb16``, Mesa sometimes not being compiled with
|
||||
float texture support, and some OSX setups being very slow with ``rgb16``
|
||||
float texture support, and some OS X setups being very slow with ``rgb16``
|
||||
but fast with ``rgb32f``.
|
||||
|
||||
``opengl-old``
|
||||
@@ -706,7 +734,7 @@ Available video output drivers are:
|
||||
2
|
||||
Convert side by side input to full-color green-magenta stereo.
|
||||
3
|
||||
Convert side by side input to quadbuffered stereo. Only supported
|
||||
Convert side by side input to quad buffered stereo. Only supported
|
||||
by very few OpenGL cards.
|
||||
|
||||
The following options are only useful if writing your own fragment programs.
|
||||
@@ -748,7 +776,7 @@ Available video output drivers are:
|
||||
auto
|
||||
auto-select (default)
|
||||
cocoa
|
||||
Cocoa/OSX
|
||||
Cocoa/OS X
|
||||
win
|
||||
Win32/WGL
|
||||
x11
|
||||
|
||||
@@ -24,6 +24,8 @@ Release procedure
|
||||
|
||||
- Create and/or update the ``VERSION`` file.
|
||||
|
||||
- Update ``DOCS/client-api-changes.rst`` (on major releases).
|
||||
|
||||
- Create tag v0.X.Y.
|
||||
|
||||
- Push branch and tag to GitHub.
|
||||
|
||||
+35
-17
@@ -1,4 +1,4 @@
|
||||
This file intends to give a big picture of how mpv is structured.
|
||||
This file intends to give a big picture overview of how mpv is structured.
|
||||
|
||||
player/*.c:
|
||||
Essentially makes up the player applications, including the main() function
|
||||
@@ -31,15 +31,28 @@ player/*.c:
|
||||
* loop, or exit if no next file or quit is requested
|
||||
(see enum stop_play_reason)
|
||||
* call exit_player_with_rc()
|
||||
* run_playloop():
|
||||
* calls fill_audio_out_buffers()
|
||||
This checks whether new audio needs to be decoded, and pushes it
|
||||
to the AO.
|
||||
* calls write_video()
|
||||
Decode new video, and push it to the VO.
|
||||
* determines whether playback of the current file has ended
|
||||
* determines when to start playback after seeks
|
||||
* and calls a whole lot of other stuff
|
||||
(Really, this function does everything.)
|
||||
|
||||
Things worth saying about the playback core:
|
||||
- the currently played tracks are in sh_video and sh_audio
|
||||
- most state is in MPContext (core.h), which is not available to the
|
||||
subsystems
|
||||
- the currently played tracks are in mpctx->current_tracks, and decoder
|
||||
state in d_video/d_audio/d_sub
|
||||
- the timeline stuff is used only with MKV ordered chapters (and some other
|
||||
minor features: cue, edl)
|
||||
- most state is in MPContext (mp_core.h), which is not available to the
|
||||
subsystems
|
||||
- the other subsystems rarely call back into the frontend, and the frontend
|
||||
polls them instead (probably a good thing)
|
||||
- one exceptions are wakeup callbacks, which notify a "higher" component
|
||||
of a changed situation in a subsystem
|
||||
|
||||
I like to call the player/*.c files the "frontend".
|
||||
|
||||
@@ -103,7 +116,7 @@ options/options.h, options/options.c
|
||||
options.c. Most default values for options and MPOpts are in
|
||||
mp_default_opts at the end of options.c.
|
||||
|
||||
MPOpts is unfortunarely quite monolithic, and virtually accessed by
|
||||
MPOpts is unfortunately quite monolithic, and virtually accessed by
|
||||
everything.But some components (like video outputs and video filters) have
|
||||
their own sub-option tables separate from MPOpts.
|
||||
|
||||
@@ -111,7 +124,7 @@ options/options.h, options/options.c
|
||||
parser-mpcmd.c, and uses the option table in options.c.
|
||||
|
||||
input/input.c:
|
||||
This translates keyboard input comming from libvo and other sources (such
|
||||
This translates keyboard input comming from VOs and other sources (such
|
||||
as remote control devices like Apple IR or client API commands) to the
|
||||
key bindings listed in the user's (or the builtin) input.conf and turns
|
||||
them into items of type struct mp_cmd. These commands are queued, and read
|
||||
@@ -122,7 +135,7 @@ input/input.c:
|
||||
of input commands somewhere else.
|
||||
|
||||
common/msg.h:
|
||||
All terminal output should go through mp_msg().
|
||||
All terminal output must go through mp_msg().
|
||||
|
||||
stream/*:
|
||||
File input is implemented here. stream.h/.c provides a simple stream based
|
||||
@@ -181,7 +194,7 @@ video/out/:
|
||||
vo_opengl can pick a windowing system at runtime, e.g. the same binary can
|
||||
provide both X11 and Cocoa support on OSX.
|
||||
|
||||
VOs can be reconfigured at runtime. A config() call can change the video
|
||||
VOs can be reconfigured at runtime. A vo_config() call can change the video
|
||||
resolution and format, without destroying the window.
|
||||
|
||||
vo_vdpau and vo_opengl should be taken as reference.
|
||||
@@ -203,9 +216,12 @@ audio/filter/:
|
||||
audio/out/:
|
||||
Audio outputs.
|
||||
|
||||
Unlike VOs, AOs can't be reconfigured on a format change. Without
|
||||
--gapless-audio, even playing a new file will close and re-open the audio
|
||||
device.
|
||||
Unlike VOs, AOs can't be reconfigured on a format change. On audio format
|
||||
changes, the AO will simply be closed and re-opened.
|
||||
|
||||
There are wrappers to support for two types of audio APIs: push.c and
|
||||
pull.c. ao.c calls into one of these. They contain generic code to deal
|
||||
with the data flow these APIs impose.
|
||||
|
||||
Note that mpv synchronizes the video to the audio. That's the reason
|
||||
why buggy audio drivers can have a bad influence on playback quality.
|
||||
@@ -218,12 +234,14 @@ sub/:
|
||||
the OSD text renderer (which uses libass, and takes care of all the tricky
|
||||
fontconfig/freetype API usage and text layouting).
|
||||
|
||||
Subtitle loading is now in demux/ instead. demux_libass.c wraps loading
|
||||
.ass subtitles via libass. demux_lavf.c loads most subtitle types via
|
||||
FFmpeg. demux_subreader.c is the old MPlayer code. It's used as last
|
||||
fallback, or to handle some text subtitle types on Libav. (It also can
|
||||
load UTF-16 encoded subtitles without requiring the use of -sub-codepage.)
|
||||
demux_subreader.c should eventually go away (maybe).
|
||||
The VOs call osd.c to render OSD and subtitle (via e.g. osd_draw()). osd.c
|
||||
in turn asks dec_sub.c for subtitle overlay bitmaps, which relays the
|
||||
request to one of the sd_*.c subtitle decoders/renderers.
|
||||
|
||||
Subtitle loading is in demux/. The MPlayer subreader.c is mostly gone - parts
|
||||
of it survive in demux_subreader.c. It's used as last fallback, or to handle
|
||||
some text subtitle types on Libav. It should go away eventually. Normally,
|
||||
subtitles are loaded via demux_lavf.c.
|
||||
|
||||
The subtitles are passed to dec_sub.c and the subtitle decoders in sd_*.c
|
||||
as they are demuxed. All text subtitles are rendered by sd_ass.c. If text
|
||||
|
||||
+86
-67
@@ -1,83 +1,102 @@
|
||||
Release 0.5.0
|
||||
Release 0.6.1
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Support setting the start time relative to the start PTS with --start.
|
||||
- Use /dev/sr0 as default cdrom/dvd device on Linux.
|
||||
- Change "cache" property representation to float.
|
||||
- Split the ao_coreaudio audio output into ao_coreaudio (which uses the
|
||||
AudioUnits and the CoreAudio sound server) and ao_coreaudio_device (which uses
|
||||
direct device access).
|
||||
- Complete supported URL schemes in the zsh completion script.
|
||||
- Enable compiler optimizations (-O2) during build.
|
||||
- Allow precise seeking with percent-based seeks too.
|
||||
- Do precise seeks when clicking on the OSC seekbar.
|
||||
- Remove support for older VDA hwaccel versions on Mac OS X (requires recent
|
||||
versions of ffmpeg/libav).
|
||||
- Remove the last remains of slave mode (--slave-broken option). Support for
|
||||
reading input commands from stdin can be emulated using: --input-terminal=no
|
||||
--input-file=/dev/stdin.
|
||||
- Some key binding were remapped or removed:
|
||||
+ "LEFT" and "RIGHT" have been remapped to seek 5 seconds instead of 10.
|
||||
+ "c" for changing the YUV color space was unmapped.
|
||||
+ OSD level cycling has been remapped from "o" to "O".
|
||||
+ "o" is now mapped to the show_progress command (like "P").
|
||||
+ "PGUP" and "PGDWN" have been remapped to chapter-based seeks (the old
|
||||
bindings "@" and "!" are still available). The old behvaiour has been
|
||||
remapped to "Shift+PGUP" and "Shift+PGDWM".
|
||||
+ Increasing/decreasing audio delay have been remapped from "+" and "-" to
|
||||
"Ctrl++" and "Ctrl+-".
|
||||
+ "(" and ")" for adjusting audio balance have been unmapped.
|
||||
+ "F" for toggling the "sub-forced-only" option has been unmapped.
|
||||
+ "TAB" for cycling TS programs has been unmapped.
|
||||
+ "A" for cycling DVD angle has been unmapped.
|
||||
+ "U" for stopping playback has been unmapped.
|
||||
+ "l" and "L" for cycling quvi format have been unmapped.
|
||||
- Make it possible to build the client API (libmpv) on Mac OS X.
|
||||
- Do necessary checks for nested Wayland compositors which only have limited
|
||||
interfaces.
|
||||
- The options in the manpage have been re-organized into sections and ordered by
|
||||
usage to make it easier to navigate.
|
||||
- The "hard" framedrop mode has been removed (this behaviour is still available
|
||||
via the --vd-lavc-framedrop option).
|
||||
- Show the basename of a subtitle file only (without the directory part).
|
||||
- Don't show message on "osd" command by default (this essentially reverts a
|
||||
change introduced in the last release).
|
||||
- Add an alternative input configuration that reverts the key binding changes
|
||||
introduced by previous releases (see etc/restore-old-bindings.conf in the
|
||||
sources).
|
||||
- Automatically install example input configuration files.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Use a default color space for files with no video (this fixes color managed
|
||||
OSD messages when no video channel is present).
|
||||
- Fix key equivalent dispatching for Cocoa on Mac OS X.
|
||||
- Disable non-interleaved audio formats by default with ao_alsa, since some
|
||||
ALSA plugins don't cope well with them (non-interleaved formats can still be
|
||||
enabled using the "non-interleaved" ao_alsa option).
|
||||
- Fix deadlock in the client API when calling mpv_terminate_destroy() before
|
||||
mpv_initialize().
|
||||
- Fix flushing of filter chain (this fixes a bug where the last frames would get
|
||||
dropped on EOF, if buffered by filters).
|
||||
- Actually use the channels.conf configuration file for DVB.
|
||||
- Restore the --dvbin-file option.
|
||||
- Fix off by one error in the command parser which caused a segfault when using
|
||||
the mp.commandv() Lua function with 10 or more arguments.
|
||||
- Make stream language work with Bluray as well (the previous release
|
||||
introduced this for DVDs, but it didn't work for Blurays).
|
||||
- Don't try to create surfaces of size 0 in the VDPAU vo.
|
||||
- Don't print audio/video init failure message twice.
|
||||
- Fix playback on the root window on X11 (i.e. when using --wid=0).
|
||||
- Fix the autodetection of the input format in the vf_stereo3d filter when mpv
|
||||
is built against libav (this makes --video-stereo-mode work with libav as
|
||||
well).
|
||||
- Fix compilation with PulseAudio 1.0.
|
||||
- Fix compilation with clang.
|
||||
- Try harder to decode cover art picture only once.
|
||||
- Fix cursor autohide (--cursor-autohide) with audio-only files on file change
|
||||
(e.g. when skipping to the next playlist file).
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log 061f509..2ffdc0d`` in
|
||||
the git repository.
|
||||
|
||||
Release 0.6.0
|
||||
=============
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
- Map ESC to exiting fullscreen.
|
||||
- Run the vo inside its own thread.
|
||||
- Enable the demuxer thread by default.
|
||||
- Prefer the opengl vo over the vdpau one.
|
||||
- Complete profile names in the zsh completion script.
|
||||
- Raise dsound ao buffer size to 200ms and make it configurable.
|
||||
- Bind ctrl+c to quit, so that it works both in the terminal and the player
|
||||
window.
|
||||
- Automatically load playlists without needing --load-unsafe-playlists or
|
||||
--playlist. Unsafe URLs are still rejected (e.g. remote playlists can't load
|
||||
local files).
|
||||
- Remove quvi-format property (which never really worked).
|
||||
- Wait until all Lua scripts are loaded on start.
|
||||
- Use separate threads for input sources (terminal, lirc, joystick) instead of
|
||||
using a central I/O loop.
|
||||
- Complete values for options that take file names in the zsh completion script.
|
||||
- Use libwaio for pipe input on Windows (if you want --input-file to work on
|
||||
Windows you need libwaio as an additional dependency).
|
||||
- Remove the --volstep option (users should change the argument to the "add"
|
||||
function in input.conf instead).
|
||||
- Always show the OSD message when changing volume, gamma, panscan, etc along
|
||||
with the OSD bar.
|
||||
- When changing a property always show it on the OSD (except for "pause" and
|
||||
"fullscreen").
|
||||
- Prefer the libavcodec decoder over libmpg123 for mp3 decoding (ffmpeg/libav
|
||||
support for gapless audio works better for mpv's use-case).
|
||||
- Allow passing number of loops to --loop-file.
|
||||
- Change --keep-open option semantics to not terminate the player when playing
|
||||
or seeking past the end of the file and there's no next file to be played.
|
||||
- Disable the sndio ao by default and prefer other outputs even if it's enabled.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
- Search for config files in $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS in addition
|
||||
to ~/.mpv. The config file has also been renamed to mpv.conf (although the
|
||||
old file still works and will be loaded even if the new one exists).
|
||||
- Add "playback-time" property that returns the current playback time always
|
||||
starting at 0. This is now used to show the playback time in the OSD and the
|
||||
status line, fixing the shown playback time for transport streams that start
|
||||
at a completely random timestamp.
|
||||
- Add cache-related "cache-free", "cache-used" and "cache-idle" properties.
|
||||
- Add option to list supported protocols (--list-protocols).
|
||||
- Allow setting libavformat stream options with the --stream-lavf-o option.
|
||||
- Support running the demuxer in a separate thread and prefetch packets (this
|
||||
can be enabled using the --demuxer-thread option).
|
||||
- Add new framedrop mode that limits the frequency of new video frames displayed
|
||||
based on the display framerate (this is enabled by default, the old
|
||||
decoder-based mode can be enabled with --framedrop=decoder).
|
||||
- Add --demuxer-readahead-secs option to control the readahead buffer using a
|
||||
duration value.
|
||||
- Add estimated-frame-count and estimated-frame-number properties.
|
||||
- Add --cache-secs option which overrides the value of --demuxer-readahead-secs
|
||||
if the stream cache is active.
|
||||
- Add demuxer-cache-duration and demuxer-cache-idle properties.
|
||||
- Make --cache-file create a temporary file if the string "TMP" is passed
|
||||
instead of a file name.
|
||||
- Add support for Matroska 3D (if a file is marked as 3D using the StereoMode
|
||||
element, an automatic conversion filter is added).
|
||||
- Add --osd-playing-msg option to show a message on the OSD when the playback
|
||||
starts.
|
||||
- Add --media-title option to force the content of the media-title property.
|
||||
- Add --osd-msg1, --osd-msg2 and --osd-msg3 options to override the OSD message
|
||||
for the corresponding OSD level (before only level 3 could be overridden with
|
||||
--osd-status-message).
|
||||
- Add support for digital passthrough in the pulse ao.
|
||||
- Add support for opening DVD and Bluray directories directly.
|
||||
- Add --input-x11-keyboard option to control whether keyboard input should be
|
||||
enabled on the X11 window ot not (this is disabled by default when using the
|
||||
libmpv library).
|
||||
|
||||
This listing is not complete. There are many more bug fixes and changes. The
|
||||
complete change log can be viewed by running ``git log cb250d4..52f2f67`` in
|
||||
complete change log can be viewed by running ``git log a1be3cf..61ebe61`` in
|
||||
the git repository.
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
conv() {
|
||||
echo
|
||||
identify -format "icon: %w %h" $1
|
||||
convert $1 -depth 8 rgba:-
|
||||
identify -format "icon: %w %h" "$1"
|
||||
convert "$1" -depth 8 rgba:-
|
||||
}
|
||||
|
||||
(echo "# File generated by gen-x11-icon.sh" ;
|
||||
|
||||
+54
-53
@@ -1,23 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
case "$0" in
|
||||
*/*)
|
||||
MYDIR=${0%/*}
|
||||
;;
|
||||
*)
|
||||
MYDIR=.
|
||||
;;
|
||||
esac
|
||||
: "${MPV:=mpv}"
|
||||
: "${ILDETECT_MPV:=$MPV}"
|
||||
: "${ILDETECT_MPVFLAGS:=--start=35% --length=35}"
|
||||
: "${ILDETECT_DRY_RUN:=}"
|
||||
: "${ILDETECT_QUIET:=}"
|
||||
: "${ILDETECT_RUN_INTERLACED_ONLY:=}"
|
||||
: "${ILDETECT_FORCE_RUN:=}"
|
||||
|
||||
: ${MPV:=mpv}
|
||||
: ${ILDETECT_MPV:=$MPV}
|
||||
: ${ILDETECT_MPV:=$MPV}
|
||||
: ${ILDETECT_MPVFLAGS:=--start=35% --length=35}
|
||||
: ${ILDETECT_DRY_RUN:=}
|
||||
: ${ILDETECT_QUIET:=}
|
||||
: ${ILDETECT_RUN_INTERLACED_ONLY:=}
|
||||
: ${ILDETECT_FORCE_RUN:=}
|
||||
: ${MAKE:=make}
|
||||
# This script uses ffmpeg's "idet" filter for interlace detection. In the
|
||||
# long run this should replace ildetect.sh+ildetect.so.
|
||||
|
||||
# exit status:
|
||||
# 0 progressive
|
||||
@@ -34,57 +26,66 @@ testfun()
|
||||
--o= --vo=null --no-audio --untimed \
|
||||
$ILDETECT_MPVFLAGS \
|
||||
| { if [ -n "$ILDETECT_QUIET" ]; then cat; else tee /dev/stderr; fi } \
|
||||
| grep "Parsed_idet_0: Multi frame detection: " | tail -n 1
|
||||
| grep "Parsed_idet_0: Multi frame detection: "
|
||||
}
|
||||
|
||||
judge()
|
||||
{
|
||||
out=`testfun "$@"`
|
||||
|
||||
tff=${out##* TFF:}; tff=${tff%% *}
|
||||
bff=${out##* BFF:}; bff=${bff%% *}
|
||||
progressive=${out##* Progressive:}; progressive=${progressive%% *}
|
||||
undetermined=${out##* Undetermined:}; undetermined=${undetermined%% *}
|
||||
|
||||
case "$tff$bff$progressive$undetermined" in
|
||||
*[!0-9]*)
|
||||
echo >&2 "ERROR: Unrecognized idet output: $out"
|
||||
exit 16
|
||||
;;
|
||||
esac
|
||||
tff=0
|
||||
bff=0
|
||||
progressive=0
|
||||
undetermined=0
|
||||
while IFS= read -r out; do
|
||||
tff1=${out##* TFF:}; tff1=${tff1%% *}
|
||||
bff1=${out##* BFF:}; bff1=${bff1%% *}
|
||||
progressive1=${out##* Progressive:}; progressive1=${progressive1%% *}
|
||||
undetermined1=${out##* Undetermined:}; undetermined1=${undetermined1%% *}
|
||||
case "$tff1$bff1$progressive1$undetermined1" in
|
||||
*[!0-9]*)
|
||||
printf >&2 'ERROR: Unrecognized idet output: %s\n' "$out"
|
||||
exit 16
|
||||
;;
|
||||
esac
|
||||
tff=$((tff + tff1))
|
||||
bff=$((bff + bff1))
|
||||
progressive=$((progressive + progressive1))
|
||||
undetermined=$((undetermined + undetermined1))
|
||||
done <<EOF
|
||||
$(testfun "$@")
|
||||
EOF
|
||||
|
||||
interlaced=$((bff + tff))
|
||||
determined=$((interlaced + progressive))
|
||||
|
||||
if [ $undetermined -gt $determined ] || [ $determined -lt 250 ]; then
|
||||
if [ "$undetermined" -gt "$determined" ] || [ "$determined" -lt 250 ]; then
|
||||
echo >&2 "ERROR: Less than 50% or 250 frames are determined."
|
||||
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
|
||||
echo >&2 "Assuming interlacing."
|
||||
if [ $tff -gt $((bff * 10)) ]; then
|
||||
verdict=interlaced-tff
|
||||
elif [ $bff -gt $((tff * 10)) ]; then
|
||||
verdict=interlaced-bff
|
||||
if [ "$tff" -gt $((bff * 10)) ]; then
|
||||
verdict="interlaced-tff"
|
||||
elif [ "$bff" -gt $((tff * 10)) ]; then
|
||||
verdict="interlaced-bff"
|
||||
else
|
||||
verdict=interlaced
|
||||
verdict="interlaced"
|
||||
fi
|
||||
elif [ $((interlaced * 20)) -gt $progressive ]; then
|
||||
elif [ $((interlaced * 20)) -gt "$progressive" ]; then
|
||||
# At least 5% of the frames are interlaced!
|
||||
if [ $tff -gt $((bff * 10)) ]; then
|
||||
verdict=interlaced-tff
|
||||
elif [ $bff -gt $((tff * 10)) ]; then
|
||||
verdict=interlaced-bff
|
||||
if [ "$tff" -gt $((bff * 10)) ]; then
|
||||
verdict="interlaced-tff"
|
||||
elif [ "$bff" -gt $((tff * 10)) ]; then
|
||||
verdict="interlaced-bff"
|
||||
else
|
||||
echo >&2 "ERROR: Content is interlaced, but can't determine field order."
|
||||
[ -n "$ILDETECT_FORCE_RUN" ] || exit 8
|
||||
echo >&2 "Assuming interlacing with default field order."
|
||||
verdict=interlaced
|
||||
verdict="interlaced"
|
||||
fi
|
||||
else
|
||||
# Likely progrssive.
|
||||
verdict=progressive
|
||||
# Likely progressive
|
||||
verdict="progressive"
|
||||
fi
|
||||
|
||||
echo "$verdict"
|
||||
printf '%s\n' "$verdict"
|
||||
}
|
||||
|
||||
judge "$@" --vf-clr
|
||||
@@ -94,7 +95,7 @@ case "$verdict" in
|
||||
[ -n "$ILDETECT_RUN_INTERLACED_ONLY" ] || \
|
||||
$ILDETECT_MPV "$@"
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 0
|
||||
;;
|
||||
interlaced-tff)
|
||||
@@ -104,14 +105,14 @@ case "$verdict" in
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=top
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=top
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -123,14 +124,14 @@ case "$verdict" in
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup --field-dominance=bottom
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif --field-dominance=bottom
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -142,14 +143,14 @@ case "$verdict" in
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=pullup
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
[ -n "$ILDETECT_DRY_RUN" ] || \
|
||||
$ILDETECT_MPV "$@" --vf-pre=yadif
|
||||
r=$?
|
||||
[ $r -eq 0 ] || exit $(($r | 16))
|
||||
[ $r -eq 0 ] || exit $((r | 16))
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -265,6 +265,7 @@ sub define_matroska {
|
||||
elem('DisplayUnit', '54b2', 'uint'),
|
||||
elem('FrameRate', '2383e3', 'float'),
|
||||
elem('ColourSpace', '2eb524', 'binary'),
|
||||
elem('StereoMode', '53b8', 'uint'),
|
||||
}),
|
||||
elem('Audio', 'e1', {
|
||||
elem('SamplingFrequency', 'b5', 'float'),
|
||||
|
||||
+127
-150
@@ -17,157 +17,134 @@
|
||||
# When multiple files were specified, their info will be put into FOO_* for the
|
||||
# first file, FOO_1_* for the second file, FOO_2_* for the third file, etc.
|
||||
|
||||
case "$0" in
|
||||
mpv_identify.sh|*/mpv_identify.sh)
|
||||
# we are NOT being sourced
|
||||
case "$1" in
|
||||
'')
|
||||
__midentify__main() {
|
||||
|
||||
case "$0" in
|
||||
mpv_identify.sh|*/mpv_identify.sh)
|
||||
# we are NOT being sourced
|
||||
[ -n "$1" ] && set -- '' "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
cat >&2 <<EOF
|
||||
Usage 1 (for humans only): $0 filename.mkv
|
||||
will print all property values.
|
||||
Note that this output really shouldn't be parsed, as the
|
||||
format is subject to change.
|
||||
|
||||
Usage 2 (for use by scripts): see top of this file
|
||||
|
||||
NOTE: for mkv with ordered chapters, this may
|
||||
not always identify the specified file, but the
|
||||
file providing the first chapter. Specify
|
||||
--no-ordered-chapters to prevent this.
|
||||
EOF
|
||||
return 2
|
||||
fi
|
||||
|
||||
local LF="
|
||||
"
|
||||
|
||||
local nextprefix="$1"
|
||||
shift
|
||||
|
||||
if [ -n "$nextprefix" ]; then
|
||||
# in case of error, we always want this unset
|
||||
unset "${nextprefix}path"
|
||||
fi
|
||||
|
||||
local allprops="
|
||||
filename
|
||||
path
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
video
|
||||
angle
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
video-aspect
|
||||
fps
|
||||
width
|
||||
height
|
||||
dwidth
|
||||
dheight
|
||||
|
||||
sub
|
||||
"
|
||||
# TODO add metadata support once mpv can do it
|
||||
|
||||
local propstr="X-MIDENTIFY-START:$LF"
|
||||
local key
|
||||
for key in $allprops; do
|
||||
propstr="${propstr}X-MIDENTIFY: $key \${=$key}$LF"
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
unset "$nextprefix$key"
|
||||
done
|
||||
|
||||
local fileindex=0
|
||||
local prefix=
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
X-MIDENTIFY-START:)
|
||||
if [ -n "$nextprefix" ]; then
|
||||
prefix="$nextprefix"
|
||||
if [ "$fileindex" -gt 0 ]; then
|
||||
nextprefix="${prefix%${fileindex}_}"
|
||||
fi
|
||||
fileindex="$((fileindex+1))"
|
||||
nextprefix="${nextprefix}${fileindex}_"
|
||||
for key in $allprops; do
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
unset "$nextprefix$key"
|
||||
done
|
||||
else
|
||||
if [ "$fileindex" -gt 0 ]; then
|
||||
printf '\n'
|
||||
fi
|
||||
fileindex="$((fileindex+1))"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
set -- '' "$@"
|
||||
X-MIDENTIFY:\ *)
|
||||
local key="${line#X-MIDENTIFY: }"
|
||||
local value="${key#* }"
|
||||
key="${key%% *}"
|
||||
key="$(printf '%s\n' "$key" | tr - _)"
|
||||
if [ -n "$nextprefix" ]; then
|
||||
if [ -z "$prefix" ]; then
|
||||
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
|
||||
elif [ -n "$value" ]; then
|
||||
eval "$prefix$key"='"$value"'
|
||||
fi
|
||||
else
|
||||
if [ -n "$value" ]; then
|
||||
printf '%s=%s\n' "$key" "$value"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$(${MPV:-mpv} --term-playing-msg="$propstr" --vo=null --ao=null \
|
||||
--frames=1 --quiet --no-cache --no-config -- "$@")
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo >&2 "Usage 1 (for humans only): $0 filename.mkv"
|
||||
echo >&2 "will print all property values."
|
||||
echo >&2 "Note that this output really shouldn't be parsed, as the"
|
||||
echo >&2 "format is subject to change."
|
||||
echo >&2
|
||||
echo >&2 "Usage 2 (for use by scripts): see top of this file"
|
||||
echo >&2
|
||||
echo >&2 "NOTE: for mkv with ordered chapters, this may"
|
||||
echo >&2 "not always identify the specified file, but the"
|
||||
echo >&2 "file providing the first chapter. Specify"
|
||||
echo >&2 "--no-ordered-chapters to prevent this."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$MPV" ]; then
|
||||
MPV="mpv"
|
||||
fi
|
||||
|
||||
__midentify__LF="
|
||||
"
|
||||
|
||||
__midentify__nextprefix=$1
|
||||
shift
|
||||
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
# in case of error, we always want this unset
|
||||
eval unset $__midentify__nextprefix'path'
|
||||
fi
|
||||
|
||||
__midentify__allprops="
|
||||
filename
|
||||
path
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
video
|
||||
angle
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
video-aspect
|
||||
fps
|
||||
width
|
||||
height
|
||||
dwidth
|
||||
dheight
|
||||
|
||||
sub
|
||||
"
|
||||
# TODO add metadata support once mpv can do it
|
||||
|
||||
__midentify__propstr="X-MIDENTIFY-START:$__midentify__LF"
|
||||
for __midentify__key in $__midentify__allprops; do
|
||||
__midentify__propstr=$__midentify__propstr"X-MIDENTIFY: $__midentify__key \${=$__midentify__key}$__midentify__LF"
|
||||
__midentify__key=`echo "$__midentify__key" | tr - _`
|
||||
eval unset $__midentify__nextprefix$__midentify__key
|
||||
done
|
||||
|
||||
__midentify__output=`$MPV --term-playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet --no-cache --no-config "$@"`
|
||||
__midentify__fileindex=0
|
||||
__midentify__prefix=
|
||||
while :; do
|
||||
case "$__midentify__output" in
|
||||
'')
|
||||
break
|
||||
;;
|
||||
*$__midentify__LF*)
|
||||
__midentify__line=${__midentify__output%%$__midentify__LF*}
|
||||
__midentify__output=${__midentify__output#*$__midentify__LF}
|
||||
;;
|
||||
*)
|
||||
__midentify__line=$__midentify__output
|
||||
__midentify__output=
|
||||
;;
|
||||
esac
|
||||
case "$__midentify__line" in
|
||||
X-MIDENTIFY-START:)
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
__midentify__prefix=$__midentify__nextprefix
|
||||
if [ $__midentify__fileindex -gt 0 ]; then
|
||||
__midentify__nextprefix=${__midentify__prefix%$__midentify__fileindex\_}
|
||||
fi
|
||||
__midentify__fileindex=$(($__midentify__fileindex+1))
|
||||
__midentify__nextprefix=$__midentify__nextprefix$__midentify__fileindex\_
|
||||
for __midentify__key in $__midentify__allprops; do
|
||||
__midentify__key=`echo "$__midentify__key" | tr - _`
|
||||
eval unset $__midentify__nextprefix$__midentify__key
|
||||
done
|
||||
else
|
||||
if [ $__midentify__fileindex -gt 0 ]; then
|
||||
echo
|
||||
fi
|
||||
__midentify__fileindex=$(($__midentify__fileindex+1))
|
||||
fi
|
||||
;;
|
||||
X-MIDENTIFY:\ *)
|
||||
__midentify__key=${__midentify__line#X-MIDENTIFY:\ }
|
||||
__midentify__value=${__midentify__key#* }
|
||||
__midentify__key=${__midentify__key%% *}
|
||||
__midentify__key=`echo "$__midentify__key" | tr - _`
|
||||
if [ -n "$__midentify__nextprefix" ]; then
|
||||
if [ -z "$__midentify__prefix" ]; then
|
||||
echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:"
|
||||
elif [ -n "$__midentify__value" ]; then
|
||||
eval $__midentify__prefix$__midentify__key=\$__midentify__value
|
||||
fi
|
||||
else
|
||||
if [ -n "$__midentify__value" ]; then
|
||||
echo "$__midentify__key=$__midentify__value"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
unset __midentify__fileindex
|
||||
unset __midentify__allprops
|
||||
unset __midentify__key
|
||||
unset __midentify__LF
|
||||
unset __midentify__line
|
||||
unset __midentify__output
|
||||
unset __midentify__nextprefix
|
||||
unset __midentify__prefix
|
||||
unset __midentify__propstr
|
||||
unset __midentify__value
|
||||
__midentify__main "$@"
|
||||
+31
-18
@@ -18,10 +18,11 @@ e.g.:
|
||||
|
||||
Currently, the following event types are supported:
|
||||
|
||||
'start' <name> start of the named event
|
||||
'end' <name> end of the named event
|
||||
'value' <float> <name> a normal value (as opposed to event)
|
||||
<event> singular event
|
||||
'start' <name> start of the named event
|
||||
'end' <name> end of the named event
|
||||
'value' <float> <name> a normal value (as opposed to event)
|
||||
'event-timed' <ts> <name> singular event at the given timestamp
|
||||
<name> singular event
|
||||
|
||||
"""
|
||||
|
||||
@@ -29,19 +30,32 @@ class G:
|
||||
events = {}
|
||||
sevents = [] # events, deterministically sorted
|
||||
start = None
|
||||
# http://matplotlib.org/api/markers_api.html#module-matplotlib.markers
|
||||
markers = ["o", "8", "s", "p", "*", "h", "+", "x", "D"]
|
||||
|
||||
def find_marker():
|
||||
if len(G.markers) == 0:
|
||||
return "o"
|
||||
m = G.markers[0]
|
||||
G.markers = G.markers[1:]
|
||||
return m
|
||||
|
||||
class Event:
|
||||
pass
|
||||
|
||||
def get_event(event):
|
||||
def get_event(event, evtype):
|
||||
if event not in G.events:
|
||||
e = Event()
|
||||
G.events[event] = e
|
||||
e.name = event
|
||||
e.vals = []
|
||||
e.type = "unknown"
|
||||
e.type = evtype
|
||||
e.marker = "o"
|
||||
e.numid = len(G.events)
|
||||
G.sevents = list(G.events.values())
|
||||
G.sevents.sort(key=lambda x: x.name)
|
||||
if e.type == "event-signal":
|
||||
e.marker = find_marker()
|
||||
return G.events[event]
|
||||
|
||||
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
|
||||
@@ -54,30 +68,29 @@ for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
|
||||
G.start = ts
|
||||
ts = ts - G.start
|
||||
if event.startswith("start "):
|
||||
e = get_event(event[6:])
|
||||
e.type = "event"
|
||||
e = get_event(event[6:], "event")
|
||||
e.vals.append((ts, 0))
|
||||
e.vals.append((ts, 1))
|
||||
elif event.startswith("end "):
|
||||
e = get_event(event[4:])
|
||||
e.type = "event"
|
||||
e = get_event(event[4:], "event")
|
||||
e.vals.append((ts, 1))
|
||||
e.vals.append((ts, 0))
|
||||
elif event.startswith("value "):
|
||||
_, val, name = event.split(" ", 2)
|
||||
val = float(val)
|
||||
e = get_event(name)
|
||||
e.type = "value"
|
||||
e = get_event(name, "value")
|
||||
e.vals.append((ts, val))
|
||||
elif event.startswith("event-timed "):
|
||||
_, val, name = event.split(" ", 2)
|
||||
val = int(val) / 1000 - G.start
|
||||
e = get_event(name, "event-signal")
|
||||
e.vals.append((val, 1))
|
||||
else:
|
||||
e = get_event(event)
|
||||
e.type = "event-signal"
|
||||
e = get_event(event, "event-signal")
|
||||
e.vals.append((ts, 1))
|
||||
|
||||
ao_events = ["ao fill", "audio wait"]
|
||||
for e in G.sevents:
|
||||
if e.name in ao_events:
|
||||
e.vals = [(x, y * 0.5) for (x, y) in e.vals]
|
||||
e.vals = [(x, y * e.numid / len(G.events)) for (x, y) in e.vals]
|
||||
|
||||
plot.hold(True)
|
||||
mainpl = plot.subplot(2, 1, 1)
|
||||
@@ -89,7 +102,7 @@ for e in G.sevents:
|
||||
plot.subplot(2, 1, 1)
|
||||
pl, = plot.plot([x for x,y in e.vals], [y for x,y in e.vals], label=e.name)
|
||||
if e.type == "event-signal":
|
||||
plot.setp(pl, marker = "o", linestyle = "None")
|
||||
plot.setp(pl, marker = e.marker, linestyle = "None")
|
||||
legend.append(pl)
|
||||
plot.subplot(2, 1, 1)
|
||||
plot.legend(legend, [pl.get_label() for pl in legend])
|
||||
|
||||
+15
-20
@@ -23,10 +23,11 @@ Note that you can control the mpv instance by writing to the command fifo:
|
||||
|
||||
echo "cycle fullscreen" > /tmp/umpv-fifo-*
|
||||
|
||||
Note: you can supply custom options with the UMPV_OPTIONS environment variable.
|
||||
The environment variable will be split on whitespace, and each item is
|
||||
passed as option to mpv _if_ the script starts mpv. If mpv is not started
|
||||
by the script (i.e. mpv is already running), the options will be ignored.
|
||||
Note: you can supply custom mpv path and options with the MPV environment
|
||||
variable. The environment variable will be split on whitespace, and the
|
||||
first item is used as path to mpv binary and the rest is passed as options
|
||||
_if_ the script starts mpv. If mpv is not started by the script (i.e. mpv
|
||||
is already running), this will be ignored.
|
||||
|
||||
Warning:
|
||||
|
||||
@@ -45,21 +46,17 @@ import fcntl
|
||||
import stat
|
||||
import string
|
||||
|
||||
if len(sys.argv) < 1:
|
||||
sys.exit(1)
|
||||
files = sys.argv[1:]
|
||||
|
||||
# this is about the same method mpv uses to decide this
|
||||
# this is the same method mpv uses to decide this
|
||||
def is_url(filename):
|
||||
parts = filename.split(":", 1)
|
||||
parts = filename.split("://", 1)
|
||||
if len(parts) < 2:
|
||||
return False
|
||||
# protocol prefix has no special characters => it's an URL
|
||||
allowed_symbols = string.ascii_letters + string.digits + '_'
|
||||
prefix = parts[0]
|
||||
for c in prefix:
|
||||
if string.ascii_letters.find(c) < 0:
|
||||
return False
|
||||
return True
|
||||
return all(map(lambda c: c in allowed_symbols, prefix))
|
||||
|
||||
# make them absolute; also makes them safe against interpretation as options
|
||||
def make_abs(filename):
|
||||
@@ -68,9 +65,6 @@ def make_abs(filename):
|
||||
return filename
|
||||
files = [make_abs(f) for f in files]
|
||||
|
||||
opts_env = os.getenv("UMPV_OPTIONS")
|
||||
opts_env = opts_env.split() if opts_env else []
|
||||
|
||||
FIFO = "/tmp/umpv-fifo-" + os.getenv("USER")
|
||||
|
||||
fifo_fd = -1
|
||||
@@ -89,7 +83,8 @@ if fifo_fd >= 0:
|
||||
if (((st.st_mode & (stat.S_IRWXG | stat.S_IRWXO)) != 0) or
|
||||
(not stat.S_ISFIFO(st.st_mode)) or
|
||||
(st.st_uid != os.getuid())):
|
||||
print("error: command FIFO is not a FIFO or has bogus permissions")
|
||||
sys.stderr.write("error: command FIFO is not a FIFO or has bogus "
|
||||
"permissions\n")
|
||||
sys.exit(1)
|
||||
|
||||
if fifo_fd >= 0:
|
||||
@@ -112,11 +107,11 @@ else:
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
os.mkfifo(FIFO, int("0600", 8))
|
||||
os.mkfifo(FIFO, 0o600)
|
||||
|
||||
opts = ["mpv", "--no-terminal", "--force-window", "--input-file=" + FIFO]
|
||||
opts.extend(opts_env)
|
||||
opts.append("--")
|
||||
opts = (os.getenv("MPV") or "mpv").split()
|
||||
opts.extend(["--no-terminal", "--force-window", "--input-file=" + FIFO,
|
||||
"--"])
|
||||
opts.extend(files)
|
||||
|
||||
subprocess.check_call(opts)
|
||||
@@ -90,8 +90,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (argc != 1 && argc != 2)
|
||||
return -1;
|
||||
|
||||
framestep_data_t *framestep = malloc(sizeof(framestep_data_t));
|
||||
memset(framestep, 0, sizeof(*framestep));
|
||||
framestep_data_t *framestep = calloc(1,sizeof(framestep_data_t));
|
||||
|
||||
framestep->step = atoi(argv[0]);
|
||||
framestep->pos = (argc >= 2) ? atoi(argv[1]) : 0;
|
||||
|
||||
@@ -267,8 +267,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
ildetect_data_t *il = malloc(sizeof(ildetect_data_t));
|
||||
memset(il, 0, sizeof(*il));
|
||||
ildetect_data_t *il = calloc(1,sizeof(ildetect_data_t));
|
||||
|
||||
#define A(i,d) ((argc>(i) && *argv[i]) ? atof(argv[i]) : (d))
|
||||
il->method = A(0, 0);
|
||||
|
||||
@@ -348,8 +348,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
{ "yuv420p", "yuv420p" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
privdata *priv = malloc(sizeof(privdata));
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
privdata *priv = calloc(1,sizeof(privdata));
|
||||
priv->step = 8;
|
||||
if(argc >= 1)
|
||||
priv->w = atoi(argv[0]);
|
||||
|
||||
@@ -237,8 +237,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (!a0[0] || a0[1] || !a1[0] || argc > 2)
|
||||
return -1;
|
||||
|
||||
tc_data_t *tc = malloc(sizeof(tc_data_t));
|
||||
memset(tc, 0, sizeof(*tc));
|
||||
tc_data_t *tc = calloc(1,sizeof(tc_data_t));
|
||||
|
||||
if (a0[0] == 't')
|
||||
tc->firstfield = 0;
|
||||
|
||||
@@ -150,8 +150,7 @@ int vf_dlopen_getcontext(struct vf_dlopen_context *ctx, int argc, const char **a
|
||||
if (argc != 2)
|
||||
return -1;
|
||||
|
||||
tile_data_t *tile = malloc(sizeof(tile_data_t));
|
||||
memset(tile, 0, sizeof(*tile));
|
||||
tile_data_t *tile = calloc(1,sizeof(tile_data_t));
|
||||
|
||||
tile->cols = atoi(argv[0]);
|
||||
tile->rows = atoi(argv[1]);
|
||||
|
||||
@@ -10,14 +10,13 @@ video_url="$(youtube-dl \
|
||||
--user-agent="$user_agent" \
|
||||
--cookies="$cookies_file" \
|
||||
--get-url \
|
||||
"$1")"
|
||||
"$@")"
|
||||
|
||||
shift
|
||||
|
||||
mpv \
|
||||
set -f
|
||||
${MPV:-mpv} \
|
||||
--cookies \
|
||||
--cookies-file="$cookies_file" \
|
||||
--user-agent="$user_agent" \
|
||||
"$@" -- $video_url
|
||||
-- $video_url
|
||||
|
||||
rm -rf "$cookies_dir"
|
||||
+60
-9
@@ -37,14 +37,49 @@ chomp $vf_str;
|
||||
$protos_str .= qq{$_ } foreach (@protos);
|
||||
chomp $protos_str;
|
||||
|
||||
my $profile_comp = <<'EOS';
|
||||
local -a profiles
|
||||
local current
|
||||
for current in "${(@f)$($words[1] --profile=help)}"; do
|
||||
current=${current//\*/\\\*}
|
||||
current=${current//\:/\\\:}
|
||||
current=${current//\[/\\\[}
|
||||
current=${current//\]/\\\]}
|
||||
if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then
|
||||
if [[ -n $match[2] ]]; then
|
||||
current="$match[1][$match[2]]"
|
||||
else
|
||||
current="$match[1]"
|
||||
fi
|
||||
profiles=($profiles $current)
|
||||
fi
|
||||
done
|
||||
if [[ $state == profile ]]; then
|
||||
# For --show-profile, only one allowed
|
||||
if (( ${#profiles} > 0 )); then
|
||||
_values 'profile' $profiles && rc=0
|
||||
fi
|
||||
else
|
||||
# For --profile, multiple allowed
|
||||
profiles=($profiles 'help[list profiles]')
|
||||
_values -s , 'profile(s)' $profiles && rc=0
|
||||
fi
|
||||
EOS
|
||||
chomp $profile_comp;
|
||||
|
||||
my $tmpl = <<"EOS";
|
||||
#compdef mpv
|
||||
|
||||
# mpv zsh completion
|
||||
|
||||
local curcontext="\$curcontext" state state_descr line
|
||||
typeset -A opt_args
|
||||
|
||||
local rc=1
|
||||
|
||||
_arguments -C -S \\
|
||||
$opts_str
|
||||
'*:files:->mfiles'
|
||||
'*:files:->mfiles' && rc=0
|
||||
|
||||
case \$state in
|
||||
ao)
|
||||
@@ -53,7 +88,7 @@ case \$state in
|
||||
$ao_str
|
||||
)
|
||||
|
||||
_describe -t values 'audio outputs' values
|
||||
_describe -t values 'audio outputs' values && rc=0
|
||||
;;
|
||||
|
||||
vo)
|
||||
@@ -62,7 +97,7 @@ $ao_str
|
||||
$vo_str
|
||||
)
|
||||
|
||||
_describe -t values 'video outputs' values
|
||||
_describe -t values 'video outputs' values && rc=0
|
||||
;;
|
||||
|
||||
af)
|
||||
@@ -71,7 +106,7 @@ $vo_str
|
||||
$af_str
|
||||
)
|
||||
|
||||
_describe -t values 'audio filters' values
|
||||
_describe -t values 'audio filters' values && rc=0
|
||||
;;
|
||||
|
||||
vf)
|
||||
@@ -80,24 +115,37 @@ $af_str
|
||||
$vf_str
|
||||
)
|
||||
|
||||
_describe -t values 'video filters' values
|
||||
_describe -t values 'video filters' values && rc=0
|
||||
;;
|
||||
|
||||
profile|profiles)
|
||||
$profile_comp
|
||||
;;
|
||||
|
||||
files)
|
||||
compset -P '*,'
|
||||
compset -S ',*'
|
||||
_files -r ',/ \\t\\n\\-' && rc=0
|
||||
;;
|
||||
|
||||
mfiles)
|
||||
local expl
|
||||
_tags files urls
|
||||
while _tags; do
|
||||
_requested files expl 'media file' _files -g \\
|
||||
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && ret=0
|
||||
"*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
|
||||
if _requested urls; then
|
||||
while _next_label urls expl URL; do
|
||||
_urls "\$expl[@]" && ret=0
|
||||
compadd -S '' "\$expl[@]" $protos_str && ret=0
|
||||
_urls "\$expl[@]" && rc=0
|
||||
compadd -S '' "\$expl[@]" $protos_str && rc=0
|
||||
done
|
||||
fi
|
||||
(( ret )) || return 0
|
||||
(( rc )) || return 0
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
return rc
|
||||
EOS
|
||||
|
||||
print $tmpl;
|
||||
@@ -133,6 +181,9 @@ sub parse_opts {
|
||||
$entry .= '->vo' if ($1 eq '--vo');
|
||||
$entry .= '->af' if ($1 eq '--af');
|
||||
$entry .= '->vf' if ($1 eq '--vf');
|
||||
$entry .= '->profiles' if ($1 eq '--profile');
|
||||
$entry .= '->profile' if ($1 eq '--show-profile');
|
||||
$entry .= '->files' if ($line =~ /\[file\]/);
|
||||
}
|
||||
|
||||
push @list, $entry
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
#define MP_NUM_CHANNELS 8
|
||||
|
||||
|
||||
+7
-98
@@ -37,8 +37,6 @@
|
||||
#include "ad.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
|
||||
#include "compat/libav.h"
|
||||
|
||||
struct priv {
|
||||
AVCodecContext *avctx;
|
||||
AVFrame *avframe;
|
||||
@@ -73,74 +71,6 @@ const struct m_sub_options ad_lavc_conf = {
|
||||
},
|
||||
};
|
||||
|
||||
struct pcm_map
|
||||
{
|
||||
int tag;
|
||||
const char *codecs[6]; // {any, 1byte, 2bytes, 3bytes, 4bytes, 8bytes}
|
||||
};
|
||||
|
||||
// NOTE: these are needed to make rawaudio with demux_mkv work.
|
||||
static const struct pcm_map tag_map[] = {
|
||||
// Microsoft PCM
|
||||
{0x0, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
|
||||
{0x1, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
|
||||
// MS PCM, Extended
|
||||
{0xfffe, {NULL, "pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"}},
|
||||
// IEEE float
|
||||
{0x3, {"pcm_f32le", [5] = "pcm_f64le"}},
|
||||
// 'raw '
|
||||
{0x20776172, {"pcm_s16be", [1] = "pcm_u8"}},
|
||||
// 'twos', used by demux_mkv.c internally
|
||||
{MKTAG('t', 'w', 'o', 's'),
|
||||
{NULL, "pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"}},
|
||||
{-1},
|
||||
};
|
||||
|
||||
// For demux_rawaudio.c; needed because ffmpeg doesn't have these sample
|
||||
// formats natively.
|
||||
static const struct pcm_map af_map[] = {
|
||||
{AF_FORMAT_U8, {"pcm_u8"}},
|
||||
{AF_FORMAT_S8, {"pcm_u8"}},
|
||||
{AF_FORMAT_U16_LE, {"pcm_u16le"}},
|
||||
{AF_FORMAT_U16_BE, {"pcm_u16be"}},
|
||||
{AF_FORMAT_S16_LE, {"pcm_s16le"}},
|
||||
{AF_FORMAT_S16_BE, {"pcm_s16be"}},
|
||||
{AF_FORMAT_U24_LE, {"pcm_u24le"}},
|
||||
{AF_FORMAT_U24_BE, {"pcm_u24be"}},
|
||||
{AF_FORMAT_S24_LE, {"pcm_s24le"}},
|
||||
{AF_FORMAT_S24_BE, {"pcm_s24be"}},
|
||||
{AF_FORMAT_U32_LE, {"pcm_u32le"}},
|
||||
{AF_FORMAT_U32_BE, {"pcm_u32be"}},
|
||||
{AF_FORMAT_S32_LE, {"pcm_s32le"}},
|
||||
{AF_FORMAT_S32_BE, {"pcm_s32be"}},
|
||||
{AF_FORMAT_FLOAT_LE, {"pcm_f32le"}},
|
||||
{AF_FORMAT_FLOAT_BE, {"pcm_f32be"}},
|
||||
{AF_FORMAT_DOUBLE_LE, {"pcm_f64le"}},
|
||||
{AF_FORMAT_DOUBLE_BE, {"pcm_f64be"}},
|
||||
{-1},
|
||||
};
|
||||
|
||||
static const char *find_pcm_decoder(const struct pcm_map *map, int format,
|
||||
int bits_per_sample)
|
||||
{
|
||||
int bytes = (bits_per_sample + 7) / 8;
|
||||
if (bytes == 8)
|
||||
bytes = 5; // 64 bit entry
|
||||
for (int n = 0; map[n].tag != -1; n++) {
|
||||
const struct pcm_map *entry = &map[n];
|
||||
if (entry->tag == format) {
|
||||
const char *dec = NULL;
|
||||
if (bytes >= 1 && bytes <= 5)
|
||||
dec = entry->codecs[bytes];
|
||||
if (!dec)
|
||||
dec = entry->codecs[0];
|
||||
if (dec)
|
||||
return dec;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_data_from_avframe(struct dec_audio *da)
|
||||
{
|
||||
struct priv *priv = da->priv;
|
||||
@@ -172,18 +102,6 @@ static void set_data_from_avframe(struct dec_audio *da)
|
||||
da->decoded.planes[n] = priv->avframe->data[n];
|
||||
}
|
||||
|
||||
static void set_from_wf(AVCodecContext *avctx, MP_WAVEFORMATEX *wf)
|
||||
{
|
||||
avctx->channels = wf->nChannels;
|
||||
avctx->sample_rate = wf->nSamplesPerSec;
|
||||
avctx->bit_rate = wf->nAvgBytesPerSec * 8;
|
||||
avctx->block_align = wf->nBlockAlign;
|
||||
avctx->bits_per_coded_sample = wf->wBitsPerSample;
|
||||
|
||||
if (wf->cbSize > 0)
|
||||
mp_lavc_set_extradata(avctx, wf + 1, wf->cbSize);
|
||||
}
|
||||
|
||||
static int init(struct dec_audio *da, const char *decoder)
|
||||
{
|
||||
struct MPOpts *mpopts = da->opts;
|
||||
@@ -196,13 +114,7 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
struct priv *ctx = talloc_zero(NULL, struct priv);
|
||||
da->priv = ctx;
|
||||
|
||||
if (sh_audio->wf && strcmp(decoder, "pcm") == 0) {
|
||||
decoder = find_pcm_decoder(tag_map, sh->format,
|
||||
sh_audio->wf->wBitsPerSample);
|
||||
} else if (sh_audio->wf && strcmp(decoder, "mp-pcm") == 0) {
|
||||
decoder = find_pcm_decoder(af_map, sh->format, 0);
|
||||
ctx->force_channel_map = true;
|
||||
}
|
||||
ctx->force_channel_map = sh_audio->force_channels;
|
||||
|
||||
lavc_codec = avcodec_find_decoder_by_name(decoder);
|
||||
if (!lavc_codec) {
|
||||
@@ -232,12 +144,13 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
lavc_context->codec_tag = sh->format;
|
||||
lavc_context->sample_rate = sh_audio->samplerate;
|
||||
lavc_context->bit_rate = sh_audio->bitrate;
|
||||
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
|
||||
lavc_context->block_align = sh_audio->block_align;
|
||||
lavc_context->bits_per_coded_sample = sh_audio->bits_per_coded_sample;
|
||||
lavc_context->channels = sh_audio->channels.num;
|
||||
if (!mp_chmap_is_unknown(&sh_audio->channels))
|
||||
lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
|
||||
|
||||
if (sh_audio->wf)
|
||||
set_from_wf(lavc_context, sh_audio->wf);
|
||||
|
||||
// demux_mkv, demux_mpg
|
||||
// demux_mkv
|
||||
if (sh_audio->codecdata_len && sh_audio->codecdata &&
|
||||
!lavc_context->extradata) {
|
||||
mp_lavc_set_extradata(lavc_context, sh_audio->codecdata,
|
||||
@@ -258,8 +171,6 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
|
||||
if (lavc_context->bit_rate != 0)
|
||||
da->bitrate = lavc_context->bit_rate;
|
||||
else if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec)
|
||||
da->bitrate = sh_audio->wf->nAvgBytesPerSec * 8;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -362,8 +273,6 @@ static int decode_packet(struct dec_audio *da)
|
||||
static void add_decoders(struct mp_decoder_list *list)
|
||||
{
|
||||
mp_add_lavc_decoders(list, AVMEDIA_TYPE_AUDIO);
|
||||
mp_add_decoder(list, "lavc", "pcm", "pcm", "Raw PCM");
|
||||
mp_add_decoder(list, "lavc", "mp-pcm", "mp-pcm", "Raw PCM");
|
||||
}
|
||||
|
||||
const struct ad_functions ad_lavc = {
|
||||
|
||||
@@ -117,13 +117,13 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
switch (stream->codec->codec_id) {
|
||||
case AV_CODEC_ID_AAC:
|
||||
spdif_ctx->iec61937_packet_size = 16384;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sample_format = AF_FORMAT_S_AAC;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_AC3:
|
||||
spdif_ctx->iec61937_packet_size = 6144;
|
||||
sample_format = AF_FORMAT_AC3_LE;
|
||||
sample_format = AF_FORMAT_S_AC3;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
@@ -131,31 +131,31 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
if (da->opts->dtshd) {
|
||||
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sample_format = AF_FORMAT_S_DTSHD;
|
||||
samplerate = 192000;
|
||||
num_channels = 2*4;
|
||||
} else {
|
||||
spdif_ctx->iec61937_packet_size = 32768;
|
||||
sample_format = AF_FORMAT_AC3_LE;
|
||||
sample_format = AF_FORMAT_S_DTS;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
}
|
||||
break;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
spdif_ctx->iec61937_packet_size = 24576;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sample_format = AF_FORMAT_S_EAC3;
|
||||
samplerate = 192000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_MP3:
|
||||
spdif_ctx->iec61937_packet_size = 4608;
|
||||
sample_format = AF_FORMAT_MPEG2;
|
||||
sample_format = AF_FORMAT_S_MP3;
|
||||
samplerate = 48000;
|
||||
num_channels = 2;
|
||||
break;
|
||||
case AV_CODEC_ID_TRUEHD:
|
||||
spdif_ctx->iec61937_packet_size = 61440;
|
||||
sample_format = AF_FORMAT_IEC61937_LE;
|
||||
sample_format = AF_FORMAT_S_TRUEHD;
|
||||
samplerate = 192000;
|
||||
num_channels = 8;
|
||||
break;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "config.h"
|
||||
#include "common/codecs.h"
|
||||
#include "common/msg.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "demux/demux.h"
|
||||
@@ -48,10 +48,10 @@ extern const struct ad_functions ad_lavc;
|
||||
extern const struct ad_functions ad_spdif;
|
||||
|
||||
static const struct ad_functions * const ad_drivers[] = {
|
||||
&ad_lavc,
|
||||
#if HAVE_MPG123
|
||||
&ad_mpg123,
|
||||
#endif
|
||||
&ad_lavc,
|
||||
&ad_spdif,
|
||||
NULL
|
||||
};
|
||||
|
||||
+20
-49
@@ -48,20 +48,12 @@ static int filter_##name(struct af_instance *af, struct mp_audio *data, int f) \
|
||||
}
|
||||
|
||||
FILTER(f, float)
|
||||
FILTER(fbe, float)
|
||||
FILTER(fle, float)
|
||||
FILTER(s32be, int32_t)
|
||||
FILTER(u32be, uint32_t)
|
||||
FILTER(s32le, int32_t)
|
||||
FILTER(u32le, uint32_t)
|
||||
FILTER(s24be, bs2b_int24_t)
|
||||
FILTER(u24be, bs2b_uint24_t)
|
||||
FILTER(s24le, bs2b_int24_t)
|
||||
FILTER(u24le, bs2b_uint24_t)
|
||||
FILTER(s16be, int16_t)
|
||||
FILTER(u16be, uint16_t)
|
||||
FILTER(s16le, int16_t)
|
||||
FILTER(u16le, uint16_t)
|
||||
FILTER(s32, int32_t)
|
||||
FILTER(u32, uint32_t)
|
||||
FILTER(s24, bs2b_int24_t)
|
||||
FILTER(u24, bs2b_uint24_t)
|
||||
FILTER(s16, int16_t)
|
||||
FILTER(u16, uint16_t)
|
||||
FILTER(s8, int8_t)
|
||||
FILTER(u8, uint8_t)
|
||||
|
||||
@@ -85,47 +77,26 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
/* check for formats supported by libbs2b
|
||||
and assign corresponding handlers */
|
||||
switch (format) {
|
||||
case AF_FORMAT_FLOAT_BE:
|
||||
af->filter = filter_fbe;
|
||||
case AF_FORMAT_FLOAT:
|
||||
af->filter = filter_f;
|
||||
break;
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
af->filter = filter_fle;
|
||||
case AF_FORMAT_S32:
|
||||
af->filter = filter_s32;
|
||||
break;
|
||||
case AF_FORMAT_S32_BE:
|
||||
af->filter = filter_s32be;
|
||||
case AF_FORMAT_U32:
|
||||
af->filter = filter_u32;
|
||||
break;
|
||||
case AF_FORMAT_U32_BE:
|
||||
af->filter = filter_u32be;
|
||||
case AF_FORMAT_S24:
|
||||
af->filter = filter_s24;
|
||||
break;
|
||||
case AF_FORMAT_S32_LE:
|
||||
af->filter = filter_s32le;
|
||||
case AF_FORMAT_U24:
|
||||
af->filter = filter_u24;
|
||||
break;
|
||||
case AF_FORMAT_U32_LE:
|
||||
af->filter = filter_u32le;
|
||||
case AF_FORMAT_S16:
|
||||
af->filter = filter_s16;
|
||||
break;
|
||||
case AF_FORMAT_S24_BE:
|
||||
af->filter = filter_s24be;
|
||||
break;
|
||||
case AF_FORMAT_U24_BE:
|
||||
af->filter = filter_u24be;
|
||||
break;
|
||||
case AF_FORMAT_S24_LE:
|
||||
af->filter = filter_s24le;
|
||||
break;
|
||||
case AF_FORMAT_U24_LE:
|
||||
af->filter = filter_u24le;
|
||||
break;
|
||||
case AF_FORMAT_S16_BE:
|
||||
af->filter = filter_s16be;
|
||||
break;
|
||||
case AF_FORMAT_U16_BE:
|
||||
af->filter = filter_u16be;
|
||||
break;
|
||||
case AF_FORMAT_S16_LE:
|
||||
af->filter = filter_s16le;
|
||||
break;
|
||||
case AF_FORMAT_U16_LE:
|
||||
af->filter = filter_u16le;
|
||||
case AF_FORMAT_U16:
|
||||
af->filter = filter_u16;
|
||||
break;
|
||||
case AF_FORMAT_S8:
|
||||
af->filter = filter_s8;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "af.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
|
||||
@@ -20,21 +20,16 @@
|
||||
|
||||
#include "af.h"
|
||||
#include "audio/format.h"
|
||||
#include "compat/mpbswap.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
static bool test_conversion(int src_format, int dst_format)
|
||||
{
|
||||
if ((src_format & AF_FORMAT_PLANAR) ||
|
||||
(dst_format & AF_FORMAT_PLANAR))
|
||||
return false;
|
||||
int src_noend = src_format & ~AF_FORMAT_END_MASK;
|
||||
int dst_noend = dst_format & ~AF_FORMAT_END_MASK;
|
||||
// We can swap endian for all formats, but sign only for integer formats.
|
||||
if (src_noend == dst_noend)
|
||||
return true;
|
||||
if (((src_noend & ~AF_FORMAT_SIGN_MASK) ==
|
||||
(dst_noend & ~AF_FORMAT_SIGN_MASK)) &&
|
||||
((src_noend & AF_FORMAT_POINT_MASK) == AF_FORMAT_I))
|
||||
if (((src_format & ~AF_FORMAT_SIGN_MASK) ==
|
||||
(dst_format & ~AF_FORMAT_SIGN_MASK)) &&
|
||||
((src_format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_I))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -63,34 +58,11 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
return AF_UNKNOWN;
|
||||
}
|
||||
|
||||
static void endian(void *data, int len, int bps)
|
||||
{
|
||||
switch (bps) {
|
||||
case 2:
|
||||
for (int i = 0; i < len; i++) {
|
||||
((uint16_t*)data)[i] = bswap_16(((uint16_t *)data)[i]);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for(int i = 0; i < len; i++) {
|
||||
uint8_t s = ((uint8_t *)data)[3 * i];
|
||||
((uint8_t *)data)[3 * i] = ((uint8_t *)data)[3 * i + 2];
|
||||
((uint8_t *)data)[3 * i + 2] = s;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for(int i = 0; i < len; i++) {
|
||||
((uint32_t*)data)[i] = bswap_32(((uint32_t *)data)[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void si2us(void *data, int len, int bps, bool le)
|
||||
static void si2us(void *data, int len, int bps)
|
||||
{
|
||||
ptrdiff_t i = -(len * bps);
|
||||
uint8_t *p = &((uint8_t *)data)[len * bps];
|
||||
if (le && bps > 1)
|
||||
if (BYTE_ORDER == LITTLE_ENDIAN && bps > 1)
|
||||
p += bps - 1;
|
||||
if (len <= 0)
|
||||
return;
|
||||
@@ -105,12 +77,8 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
int outfmt = af->data->format;
|
||||
size_t len = data->samples * data->nch;
|
||||
|
||||
if ((infmt & AF_FORMAT_END_MASK) != (outfmt & AF_FORMAT_END_MASK))
|
||||
endian(data->planes[0], len, data->bps);
|
||||
|
||||
if ((infmt & AF_FORMAT_SIGN_MASK) != (outfmt & AF_FORMAT_SIGN_MASK))
|
||||
si2us(data->planes[0], len, data->bps,
|
||||
(outfmt & AF_FORMAT_END_MASK) == AF_FORMAT_LE);
|
||||
si2us(data->planes[0], len, data->bps);
|
||||
|
||||
mp_audio_set_format(data, outfmt);
|
||||
return 0;
|
||||
@@ -124,8 +92,8 @@ static int af_open(struct af_instance *af)
|
||||
}
|
||||
|
||||
const struct af_info af_info_convertsignendian = {
|
||||
.info = "Convert between sample format sign/endian",
|
||||
.name = "convertsignendian",
|
||||
.info = "Convert between sample format sign",
|
||||
.name = "convertsign",
|
||||
.open = af_open,
|
||||
.test_conversion = test_conversion,
|
||||
};
|
||||
+34
-16
@@ -284,6 +284,27 @@ static inline void update_ch(af_hrtf_t *s, short *in, const int k)
|
||||
s->ba_r[k] = in[4] + in[1] + in[3];
|
||||
}
|
||||
|
||||
static void clear_coeff(af_hrtf_t *s, float *c)
|
||||
{
|
||||
memset(c, 0, s->dlbuflen * sizeof(float));
|
||||
}
|
||||
|
||||
static void reset(af_hrtf_t *s)
|
||||
{
|
||||
clear_coeff(s, s->lf);
|
||||
clear_coeff(s, s->rf);
|
||||
clear_coeff(s, s->lr);
|
||||
clear_coeff(s, s->rr);
|
||||
clear_coeff(s, s->cf);
|
||||
clear_coeff(s, s->cr);
|
||||
clear_coeff(s, s->ba_l);
|
||||
clear_coeff(s, s->ba_r);
|
||||
clear_coeff(s, s->fwrbuf_l);
|
||||
clear_coeff(s, s->fwrbuf_r);
|
||||
clear_coeff(s, s->fwrbuf_lr);
|
||||
clear_coeff(s, s->fwrbuf_rr);
|
||||
}
|
||||
|
||||
/* Initialization and runtime control */
|
||||
static int control(struct af_instance *af, int cmd, void* arg)
|
||||
{
|
||||
@@ -292,30 +313,27 @@ static int control(struct af_instance *af, int cmd, void* arg)
|
||||
|
||||
switch(cmd) {
|
||||
case AF_CONTROL_REINIT:
|
||||
af->data->rate = ((struct mp_audio*)arg)->rate;
|
||||
if(af->data->rate != 48000) {
|
||||
// automatic samplerate adjustment in the filter chain
|
||||
// is not yet supported.
|
||||
MP_ERR(af, "ERROR: Sampling rate is not 48000 Hz (%d)!\n",
|
||||
af->data->rate);
|
||||
return AF_ERROR;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
} else if (af->data->nch < 5) {
|
||||
mp_audio_set_channels_old(af->data, 5);
|
||||
}
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S16);
|
||||
test_output_res = af_test_output(af, (struct mp_audio*)arg);
|
||||
// after testing input set the real output format
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
s->print_flag = 1;
|
||||
return test_output_res;
|
||||
case AF_CONTROL_RESET:
|
||||
reset(s);
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
return AF_UNKNOWN;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <libavutil/audioconvert.h>
|
||||
#include <libavutil/intreadwrite.h>
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/bswap.h>
|
||||
#include <libavutil/mem.h>
|
||||
|
||||
#include "common/common.h"
|
||||
@@ -87,7 +88,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
|
||||
if (in->nch > AC3_MAX_CHANNELS)
|
||||
mp_audio_set_num_channels(in, AC3_MAX_CHANNELS);
|
||||
|
||||
mp_audio_set_format(af->data, AF_FORMAT_AC3_BE);
|
||||
mp_audio_set_format(af->data, AF_FORMAT_S_AC3);
|
||||
mp_audio_set_num_channels(af->data, 2);
|
||||
|
||||
if (!mp_audio_config_equals(in, &orig_in))
|
||||
@@ -150,6 +151,12 @@ static void uninit(struct af_instance* af)
|
||||
}
|
||||
}
|
||||
|
||||
static void swap_16(uint16_t *ptr, size_t size)
|
||||
{
|
||||
for (size_t n = 0; n < size; n++)
|
||||
ptr[n] = av_bswap16(ptr[n]);
|
||||
}
|
||||
|
||||
// Filter data through filter
|
||||
static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
|
||||
{
|
||||
@@ -223,11 +230,11 @@ static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
|
||||
frame_size = AC3_FRAME_SIZE * 2 * 2;
|
||||
header_len = 8;
|
||||
|
||||
AV_WB16(hdr, 0xF872); // iec 61937 syncword 1
|
||||
AV_WB16(hdr + 2, 0x4E1F); // iec 61937 syncword 2
|
||||
hdr[4] = bsmod; // bsmod
|
||||
hdr[5] = 0x01; // data-type ac3
|
||||
AV_WB16(hdr + 6, len << 3); // number of bits in payload
|
||||
AV_WL16(hdr, 0xF872); // iec 61937 syncword 1
|
||||
AV_WL16(hdr + 2, 0x4E1F); // iec 61937 syncword 2
|
||||
hdr[5] = bsmod; // bsmod
|
||||
hdr[4] = 0x01; // data-type ac3
|
||||
AV_WL16(hdr + 6, len << 3); // number of bits in payload
|
||||
}
|
||||
|
||||
size_t max_size = (max_out_samples - out->samples) * out->sstride;
|
||||
@@ -239,6 +246,7 @@ static int filter(struct af_instance* af, struct mp_audio* audio, int flags)
|
||||
memcpy(buf + header_len, s->pkt.data, s->pkt.size);
|
||||
memset(buf + header_len + s->pkt.size, 0,
|
||||
frame_size - (header_len + s->pkt.size));
|
||||
swap_16((uint16_t *)(buf + header_len), s->pkt.size / 2);
|
||||
out->samples += frame_size / out->sstride;
|
||||
}
|
||||
|
||||
@@ -312,12 +320,13 @@ const struct af_info af_info_lavcac3enc = {
|
||||
.priv_size = sizeof(struct af_ac3enc_s),
|
||||
.priv_defaults = &(const struct af_ac3enc_s){
|
||||
.cfg_add_iec61937_header = 1,
|
||||
.cfg_min_channel_num = 5,
|
||||
.cfg_bit_rate = 640,
|
||||
.cfg_min_channel_num = 3,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_FLAG("tospdif", cfg_add_iec61937_header, 0),
|
||||
OPT_CHOICE_OR_INT("bitrate", cfg_bit_rate, 0, 32, 640,
|
||||
({"default", 0})),
|
||||
({"auto", 0}, {"default", 0})),
|
||||
OPT_INTRANGE("minch", cfg_min_channel_num, 0, 2, 6),
|
||||
{0}
|
||||
},
|
||||
|
||||
@@ -214,8 +214,7 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
|
||||
if (avresample_open(s->avrctx) < 0 ||
|
||||
avresample_open(s->avrctx_out) < 0)
|
||||
{
|
||||
MP_ERR(af, "Cannot open "
|
||||
"Libavresample Context. \n");
|
||||
MP_ERR(af, "Cannot open Libavresample Context. \n");
|
||||
return AF_ERROR;
|
||||
}
|
||||
return AF_OK;
|
||||
@@ -339,7 +338,7 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
if (needs_reorder(s->reorder_out, out->nch)) {
|
||||
if (af_fmt_is_planar(out->format)) {
|
||||
reorder_planes(data, s->reorder_out);
|
||||
} else {
|
||||
} else if (out->samples) {
|
||||
int out_size = out->samples * out->sstride;
|
||||
if (talloc_get_size(s->reorder_buffer) < out_size)
|
||||
s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size);
|
||||
@@ -347,7 +346,8 @@ static int filter(struct af_instance *af, struct mp_audio *data, int flags)
|
||||
int out_samples = avresample_convert(s->avrctx_out,
|
||||
(uint8_t **) data->planes, out_size, out->samples,
|
||||
(uint8_t **) out->planes, out_size, out->samples);
|
||||
assert(out_samples == data->samples);
|
||||
if (out_samples < 0)
|
||||
MP_ERR(af, "Reordering failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,8 +371,7 @@ static int af_open(struct af_instance *af)
|
||||
if (s->avrctx && s->avrctx_out) {
|
||||
return AF_OK;
|
||||
} else {
|
||||
MP_ERR(af, "Cannot initialize "
|
||||
"Libavresample Context. \n");
|
||||
MP_ERR(af, "Cannot initialize Libavresample Context. \n");
|
||||
uninit(af);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
+26
-38
@@ -30,9 +30,6 @@
|
||||
|
||||
int af_fmt2bps(int format)
|
||||
{
|
||||
if (AF_FORMAT_IS_AC3(format)) return 2;
|
||||
if (format == AF_FORMAT_UNKNOWN)
|
||||
return 0;
|
||||
switch (format & AF_FORMAT_BITS_MASK) {
|
||||
case AF_FORMAT_8BIT: return 1;
|
||||
case AF_FORMAT_16BIT: return 2;
|
||||
@@ -62,7 +59,7 @@ static int bits_to_mask(int bits)
|
||||
|
||||
int af_fmt_change_bits(int format, int bits)
|
||||
{
|
||||
if (!af_fmt_is_valid(format) || (format & AF_FORMAT_SPECIAL_MASK))
|
||||
if (!af_fmt_is_valid(format))
|
||||
return 0;
|
||||
int mask = bits_to_mask(bits);
|
||||
format = (format & ~AF_FORMAT_BITS_MASK) | mask;
|
||||
@@ -109,35 +106,31 @@ bool af_fmt_is_planar(int format)
|
||||
return !!(format & AF_FORMAT_PLANAR);
|
||||
}
|
||||
|
||||
#define FMT(string, id) \
|
||||
{string, id},
|
||||
|
||||
#define FMT_ENDIAN(string, id) \
|
||||
{string, id}, \
|
||||
{string "le", MP_CONCAT(id, _LE)}, \
|
||||
{string "be", MP_CONCAT(id, _BE)}, \
|
||||
|
||||
const struct af_fmt_entry af_fmtstr_table[] = {
|
||||
FMT("mpeg2", AF_FORMAT_MPEG2)
|
||||
FMT_ENDIAN("ac3", AF_FORMAT_AC3)
|
||||
FMT_ENDIAN("iec61937", AF_FORMAT_IEC61937)
|
||||
{"u8", AF_FORMAT_U8},
|
||||
{"s8", AF_FORMAT_S8},
|
||||
{"u16", AF_FORMAT_U16},
|
||||
{"s16", AF_FORMAT_S16},
|
||||
{"u24", AF_FORMAT_U24},
|
||||
{"s24", AF_FORMAT_S24},
|
||||
{"u32", AF_FORMAT_U32},
|
||||
{"s32", AF_FORMAT_S32},
|
||||
{"float", AF_FORMAT_FLOAT},
|
||||
{"double", AF_FORMAT_DOUBLE},
|
||||
|
||||
FMT("u8", AF_FORMAT_U8)
|
||||
FMT("s8", AF_FORMAT_S8)
|
||||
FMT_ENDIAN("u16", AF_FORMAT_U16)
|
||||
FMT_ENDIAN("s16", AF_FORMAT_S16)
|
||||
FMT_ENDIAN("u24", AF_FORMAT_U24)
|
||||
FMT_ENDIAN("s24", AF_FORMAT_S24)
|
||||
FMT_ENDIAN("u32", AF_FORMAT_U32)
|
||||
FMT_ENDIAN("s32", AF_FORMAT_S32)
|
||||
FMT_ENDIAN("float", AF_FORMAT_FLOAT)
|
||||
FMT_ENDIAN("double", AF_FORMAT_DOUBLE)
|
||||
{"u8p", AF_FORMAT_U8P},
|
||||
{"s16p", AF_FORMAT_S16P},
|
||||
{"s32p", AF_FORMAT_S32P},
|
||||
{"floatp", AF_FORMAT_FLOATP},
|
||||
{"doublep", AF_FORMAT_DOUBLEP},
|
||||
|
||||
FMT("u8p", AF_FORMAT_U8P)
|
||||
FMT("s16p", AF_FORMAT_S16P)
|
||||
FMT("s32p", AF_FORMAT_S32P)
|
||||
FMT("floatp", AF_FORMAT_FLOATP)
|
||||
FMT("doublep", AF_FORMAT_DOUBLEP)
|
||||
{"spdif-aac", AF_FORMAT_S_AAC},
|
||||
{"spdif-ac3", AF_FORMAT_S_AC3},
|
||||
{"spdif-dts", AF_FORMAT_S_DTS},
|
||||
{"spdif-dtshd", AF_FORMAT_S_DTSHD},
|
||||
{"spdif-eac3", AF_FORMAT_S_EAC3},
|
||||
{"spdif-mp3", AF_FORMAT_S_MP3},
|
||||
{"spdif-truehd",AF_FORMAT_S_TRUEHD},
|
||||
|
||||
{0}
|
||||
};
|
||||
@@ -199,22 +192,17 @@ int af_format_conversion_score(int dst_format, int src_format)
|
||||
return INT_MIN;
|
||||
if (dst_format == src_format)
|
||||
return 1024;
|
||||
// Just endian swapping (separate because it works for special formats)
|
||||
if ((dst_format & ~AF_FORMAT_END_MASK) == (src_format & ~AF_FORMAT_END_MASK))
|
||||
return 1024 - 2;
|
||||
// Can't be normally converted
|
||||
if (AF_FORMAT_IS_SPECIAL(dst_format) || AF_FORMAT_IS_SPECIAL(src_format))
|
||||
return INT_MIN;
|
||||
int score = 1024;
|
||||
if (FMT_DIFF(AF_FORMAT_INTERLEAVING_MASK, dst_format, src_format))
|
||||
score -= 1; // has to (de-)planarize
|
||||
if (FMT_DIFF(AF_FORMAT_END_MASK, dst_format, src_format))
|
||||
score -= 2; // has to swap endian
|
||||
if (FMT_DIFF(AF_FORMAT_SIGN_MASK, dst_format, src_format))
|
||||
score -= 4; // has to swap sign
|
||||
if (FMT_DIFF(AF_FORMAT_POINT_MASK, dst_format, src_format)) {
|
||||
if (FMT_DIFF(AF_FORMAT_TYPE_MASK, dst_format, src_format)) {
|
||||
int dst_bits = dst_format & AF_FORMAT_BITS_MASK;
|
||||
if ((dst_format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) {
|
||||
if ((dst_format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F) {
|
||||
// For int->float, always prefer 32 bit float.
|
||||
score -= dst_bits == AF_FORMAT_32BIT ? 8 : 0;
|
||||
} else {
|
||||
@@ -230,7 +218,7 @@ int af_format_conversion_score(int dst_format, int src_format)
|
||||
}
|
||||
}
|
||||
// Consider this the worst case.
|
||||
if (FMT_DIFF(AF_FORMAT_POINT_MASK, dst_format, src_format))
|
||||
if (FMT_DIFF(AF_FORMAT_TYPE_MASK, dst_format, src_format))
|
||||
score -= 2048; // has to convert float<->int
|
||||
return score;
|
||||
}
|
||||
+47
-91
@@ -25,121 +25,77 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "osdep/endian.h"
|
||||
#include "bstr/bstr.h"
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define AF_SELECT_LE_BE(LE, BE) BE
|
||||
#else
|
||||
#define AF_SELECT_LE_BE(LE, BE) LE
|
||||
#endif
|
||||
|
||||
// Endianness
|
||||
#define AF_FORMAT_BE (0<<0) // Big Endian
|
||||
#define AF_FORMAT_LE (1<<0) // Little Endian
|
||||
#define AF_FORMAT_END_MASK (1<<0)
|
||||
|
||||
#define AF_FORMAT_NE AF_SELECT_LE_BE(AF_FORMAT_LE, AF_FORMAT_BE)
|
||||
#include "misc/bstr.h"
|
||||
|
||||
// Signed/unsigned
|
||||
#define AF_FORMAT_SI (0<<1) // Signed
|
||||
#define AF_FORMAT_US (1<<1) // Unsigned
|
||||
#define AF_FORMAT_SIGN_MASK (1<<1)
|
||||
#define AF_FORMAT_SI (0<<0) // Signed
|
||||
#define AF_FORMAT_US (1<<0) // Unsigned
|
||||
#define AF_FORMAT_SIGN_MASK (1<<0)
|
||||
|
||||
// Bits used
|
||||
// Some code assumes they're sorted by size.
|
||||
#define AF_FORMAT_8BIT (0<<3)
|
||||
#define AF_FORMAT_16BIT (1<<3)
|
||||
#define AF_FORMAT_24BIT (2<<3)
|
||||
#define AF_FORMAT_32BIT (3<<3)
|
||||
#define AF_FORMAT_64BIT (4<<3)
|
||||
#define AF_FORMAT_BITS_MASK (7<<3)
|
||||
#define AF_FORMAT_8BIT (0<<1)
|
||||
#define AF_FORMAT_16BIT (1<<1)
|
||||
#define AF_FORMAT_24BIT (2<<1)
|
||||
#define AF_FORMAT_32BIT (3<<1)
|
||||
#define AF_FORMAT_64BIT (4<<1)
|
||||
#define AF_FORMAT_BITS_MASK (7<<1)
|
||||
|
||||
// Special flags refering to non pcm data (note: 1<<6, 2<<6, 5<<6 unused)
|
||||
#define AF_FORMAT_S_MPEG2 (3<<6) // MPEG(2) audio
|
||||
#define AF_FORMAT_S_AC3 (4<<6) // Dolby Digital AC3
|
||||
#define AF_FORMAT_S_IEC61937 (6<<6)
|
||||
#define AF_FORMAT_SPECIAL_MASK (7<<6)
|
||||
|
||||
// Fixed or floating point
|
||||
#define AF_FORMAT_I (1<<9) // Int
|
||||
#define AF_FORMAT_F (2<<9) // Foating point
|
||||
#define AF_FORMAT_POINT_MASK (3<<9)
|
||||
// Fixed/floating point/special
|
||||
#define AF_FORMAT_I (1<<4) // Int
|
||||
#define AF_FORMAT_F (2<<4) // Foating point
|
||||
#define AF_FORMAT_S (4<<4) // special (IEC61937)
|
||||
#define AF_FORMAT_TYPE_MASK (7<<4)
|
||||
|
||||
// Interleaving (planar formats have data for each channel in separate planes)
|
||||
#define AF_FORMAT_INTERLEAVED (0<<11) // must be 0
|
||||
#define AF_FORMAT_PLANAR (1<<11)
|
||||
#define AF_FORMAT_INTERLEAVING_MASK (1<<11)
|
||||
#define AF_FORMAT_INTERLEAVED (0<<7) // must be 0
|
||||
#define AF_FORMAT_PLANAR (1<<7)
|
||||
#define AF_FORMAT_INTERLEAVING_MASK (1<<7)
|
||||
|
||||
#define AF_FORMAT_S_CODEC(n) ((n)<<8)
|
||||
#define AF_FORMAT_S_CODEC_MASK (15 <<8) // 16 codecs max.
|
||||
|
||||
#define AF_FORMAT_MASK ((1<<12)-1)
|
||||
|
||||
#define AF_INTP (AF_FORMAT_I|AF_FORMAT_PLANAR)
|
||||
#define AF_FLTP (AF_FORMAT_F|AF_FORMAT_PLANAR)
|
||||
#define AF_FORMAT_S_(n) (AF_FORMAT_S_CODEC(n)|AF_FORMAT_S|AF_FORMAT_16BIT)
|
||||
|
||||
// actual sample formats
|
||||
enum af_format {
|
||||
AF_FORMAT_UNKNOWN = 0,
|
||||
|
||||
AF_FORMAT_U8 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S8 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_U16_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_U16_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_S16_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_S16_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_U24_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_U24_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_S24_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_S24_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_U32_LE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_U32_BE = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_S32_LE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_S32_BE = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_BE),
|
||||
AF_FORMAT_U8 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_S8 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_U16 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_S16 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_U24 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_24BIT),
|
||||
AF_FORMAT_S24 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_24BIT),
|
||||
AF_FORMAT_U32 = (AF_FORMAT_I|AF_FORMAT_US|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_S32 = (AF_FORMAT_I|AF_FORMAT_SI|AF_FORMAT_32BIT),
|
||||
|
||||
AF_FORMAT_FLOAT_LE = (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_FLOAT_BE = (AF_FORMAT_F|AF_FORMAT_32BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_DOUBLE_LE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_DOUBLE_BE = (AF_FORMAT_F|AF_FORMAT_64BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_AC3_LE = (AF_FORMAT_S_AC3|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_AC3_BE = (AF_FORMAT_S_AC3|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_IEC61937_LE = (AF_FORMAT_S_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_LE),
|
||||
AF_FORMAT_IEC61937_BE = (AF_FORMAT_S_IEC61937|AF_FORMAT_16BIT|AF_FORMAT_BE),
|
||||
|
||||
AF_FORMAT_MPEG2 = (AF_FORMAT_S_MPEG2),
|
||||
AF_FORMAT_FLOAT = (AF_FORMAT_F|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_DOUBLE = (AF_FORMAT_F|AF_FORMAT_64BIT),
|
||||
|
||||
// Planar variants
|
||||
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT|AF_FORMAT_NE),
|
||||
AF_FORMAT_U8P = (AF_INTP|AF_FORMAT_US|AF_FORMAT_8BIT),
|
||||
AF_FORMAT_S16P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_16BIT),
|
||||
AF_FORMAT_S32P = (AF_INTP|AF_FORMAT_SI|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_FLOATP = (AF_FLTP|AF_FORMAT_32BIT),
|
||||
AF_FORMAT_DOUBLEP = (AF_FLTP|AF_FORMAT_64BIT),
|
||||
|
||||
// Native endian variants
|
||||
AF_FORMAT_U16 = AF_SELECT_LE_BE(AF_FORMAT_U16_LE, AF_FORMAT_U16_BE),
|
||||
AF_FORMAT_S16 = AF_SELECT_LE_BE(AF_FORMAT_S16_LE, AF_FORMAT_S16_BE),
|
||||
AF_FORMAT_U24 = AF_SELECT_LE_BE(AF_FORMAT_U24_LE, AF_FORMAT_U24_BE),
|
||||
AF_FORMAT_S24 = AF_SELECT_LE_BE(AF_FORMAT_S24_LE, AF_FORMAT_S24_BE),
|
||||
AF_FORMAT_U32 = AF_SELECT_LE_BE(AF_FORMAT_U32_LE, AF_FORMAT_U32_BE),
|
||||
AF_FORMAT_S32 = AF_SELECT_LE_BE(AF_FORMAT_S32_LE, AF_FORMAT_S32_BE),
|
||||
|
||||
AF_FORMAT_FLOAT = AF_SELECT_LE_BE(AF_FORMAT_FLOAT_LE, AF_FORMAT_FLOAT_BE),
|
||||
AF_FORMAT_DOUBLE = AF_SELECT_LE_BE(AF_FORMAT_DOUBLE_LE, AF_FORMAT_DOUBLE_BE),
|
||||
|
||||
AF_FORMAT_AC3 = AF_SELECT_LE_BE(AF_FORMAT_AC3_LE, AF_FORMAT_AC3_BE),
|
||||
AF_FORMAT_IEC61937 = AF_SELECT_LE_BE(AF_FORMAT_IEC61937_LE, AF_FORMAT_IEC61937_BE),
|
||||
// All of these use IEC61937 framing, and otherwise pretend to be like PCM.
|
||||
AF_FORMAT_S_AAC = AF_FORMAT_S_(0),
|
||||
AF_FORMAT_S_AC3 = AF_FORMAT_S_(1),
|
||||
AF_FORMAT_S_DTS = AF_FORMAT_S_(2),
|
||||
AF_FORMAT_S_DTSHD = AF_FORMAT_S_(3),
|
||||
AF_FORMAT_S_EAC3 = AF_FORMAT_S_(4),
|
||||
AF_FORMAT_S_MP3 = AF_FORMAT_S_(5),
|
||||
AF_FORMAT_S_TRUEHD = AF_FORMAT_S_(6),
|
||||
};
|
||||
|
||||
#define AF_FORMAT_IS_AC3(fmt) \
|
||||
(((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_S_AC3)
|
||||
|
||||
#define AF_FORMAT_IS_IEC61937(fmt) \
|
||||
(AF_FORMAT_IS_AC3(fmt) || \
|
||||
((fmt) & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_S_IEC61937)
|
||||
|
||||
#define AF_FORMAT_IS_SPECIAL(fmt) \
|
||||
((fmt & AF_FORMAT_SPECIAL_MASK) != 0)
|
||||
#define AF_FORMAT_IS_IEC61937(f) (((f) & AF_FORMAT_TYPE_MASK) == AF_FORMAT_S)
|
||||
#define AF_FORMAT_IS_SPECIAL(f) AF_FORMAT_IS_IEC61937(f)
|
||||
|
||||
struct af_fmt_entry {
|
||||
const char *name;
|
||||
|
||||
+8
-6
@@ -37,6 +37,7 @@ struct mixer {
|
||||
struct af_stream *af;
|
||||
// Static, dependent on ao/softvol settings
|
||||
bool softvol; // use AO (true) or af_volume (false)
|
||||
bool ao_softvol; // AO has private or per-app volume
|
||||
bool emulate_mute; // if true, emulate mute with volume=0
|
||||
// Last known values (possibly out of sync with reality)
|
||||
float vol_l, vol_r;
|
||||
@@ -245,11 +246,13 @@ char *mixer_get_volume_restore_data(struct mixer *mixer)
|
||||
|
||||
static void probe_softvol(struct mixer *mixer)
|
||||
{
|
||||
mixer->ao_softvol =
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_SOFT_VOLUME, 0) == 1 ||
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) == 1;
|
||||
|
||||
if (mixer->opts->softvol == SOFTVOL_AUTO) {
|
||||
// No system-wide volume => fine with AO volume control.
|
||||
mixer->softvol =
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_TEMP_VOLUME, 0) != 1 ||
|
||||
ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) != 1;
|
||||
mixer->softvol = !mixer->ao_softvol;
|
||||
} else {
|
||||
mixer->softvol = mixer->opts->softvol == SOFTVOL_YES;
|
||||
}
|
||||
@@ -282,8 +285,7 @@ static void restore_volume(struct mixer *mixer)
|
||||
const char *prev_driver = mixer->driver;
|
||||
mixer->driver = mixer->softvol ? "softvol" : ao_get_name(ao);
|
||||
|
||||
bool restore =
|
||||
mixer->softvol || ao_control(ao, AOCONTROL_HAS_TEMP_VOLUME, 0) == 1;
|
||||
bool restore = mixer->softvol || mixer->ao_softvol;
|
||||
|
||||
// Restore old parameters if volume won't survive reinitialization.
|
||||
// But not if volume scale is possibly different.
|
||||
@@ -361,7 +363,7 @@ void mixer_uninit_audio(struct mixer *mixer)
|
||||
return;
|
||||
|
||||
checkvolume(mixer);
|
||||
if (mixer->muted_by_us) {
|
||||
if (mixer->muted_by_us && !mixer->softvol && !mixer->ao_softvol) {
|
||||
/* Current audio output API combines playing the remaining buffered
|
||||
* audio and uninitializing the AO into one operation, even though
|
||||
* ideally unmute would happen between those two steps. We can't do
|
||||
|
||||
+7
-15
@@ -60,9 +60,6 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||
#if HAVE_PULSE
|
||||
&audio_out_pulse,
|
||||
#endif
|
||||
#if HAVE_SNDIO
|
||||
&audio_out_sndio,
|
||||
#endif
|
||||
#if HAVE_ALSA
|
||||
&audio_out_alsa,
|
||||
#endif
|
||||
@@ -87,6 +84,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
|
||||
#endif
|
||||
#if HAVE_SDL1 || HAVE_SDL2
|
||||
&audio_out_sdl,
|
||||
#endif
|
||||
#if HAVE_SNDIO
|
||||
&audio_out_sndio,
|
||||
#endif
|
||||
&audio_out_null,
|
||||
// should not be auto-selected:
|
||||
@@ -152,6 +152,7 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
|
||||
.channels = channels,
|
||||
.format = format,
|
||||
.log = mp_log_new(ao, log, name),
|
||||
.def_buffer = global->opts->audio_buffer,
|
||||
};
|
||||
if (ao->driver->encode != !!ao->encode_lavc_ctx)
|
||||
goto error;
|
||||
@@ -187,7 +188,7 @@ static struct ao *ao_create(bool probing, struct mpv_global *global,
|
||||
ao->device_buffer = ao->driver->get_space(ao);
|
||||
MP_VERBOSE(ao, "device buffer: %d samples.\n", ao->device_buffer);
|
||||
}
|
||||
ao->buffer = MPMAX(ao->device_buffer, MIN_BUFFER * ao->samplerate);
|
||||
ao->buffer = MPMAX(ao->device_buffer, ao->def_buffer * ao->samplerate);
|
||||
MP_VERBOSE(ao, "using soft-buffer of %d samples.\n", ao->buffer);
|
||||
|
||||
if (ao->api->init(ao) < 0)
|
||||
@@ -258,16 +259,7 @@ int ao_play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case AOCONTROL_HAS_TEMP_VOLUME:
|
||||
return !ao->no_persistent_volume;
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return !!ao->per_application_mixer;
|
||||
default:
|
||||
if (ao->api->control)
|
||||
return ao->api->control(ao, cmd, arg);
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
return ao->api->control ? ao->api->control(ao, cmd, arg) : CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
// Return size of the buffered data in seconds. Can include the device latency.
|
||||
@@ -321,7 +313,7 @@ void ao_drain(struct ao *ao)
|
||||
|
||||
bool ao_eof_reached(struct ao *ao)
|
||||
{
|
||||
return ao_get_delay(ao) < AO_EOF_DELAY;
|
||||
return ao->api->get_eof ? ao->api->get_eof(ao) : true;
|
||||
}
|
||||
|
||||
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
|
||||
+4
-2
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "common/common.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/chmap_sel.h"
|
||||
@@ -36,7 +36,9 @@ enum aocontrol {
|
||||
AOCONTROL_SET_MUTE,
|
||||
// Has char* as argument, which contains the desired stream title.
|
||||
AOCONTROL_UPDATE_STREAM_TITLE,
|
||||
AOCONTROL_HAS_TEMP_VOLUME,
|
||||
// the AO does the equivalent of af_volume (return CONTROL_TRUE if yes)
|
||||
AOCONTROL_HAS_SOFT_VOLUME,
|
||||
// like above, but volume persists (per app), mpv won't restore volume
|
||||
AOCONTROL_HAS_PER_APP_VOLUME,
|
||||
};
|
||||
|
||||
|
||||
+25
-29
@@ -38,6 +38,7 @@
|
||||
#include "options/options.h"
|
||||
#include "options/m_option.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#define ALSA_PCM_NEW_HW_PARAMS_API
|
||||
#define ALSA_PCM_NEW_SW_PARAMS_API
|
||||
@@ -99,7 +100,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
long get_vol, set_vol;
|
||||
float f_multi;
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
if (AF_FORMAT_IS_SPECIAL(ao->format))
|
||||
return CONTROL_FALSE;
|
||||
|
||||
//allocate simple id
|
||||
@@ -208,25 +209,15 @@ alsa_error:
|
||||
static const int mp_to_alsa_format[][2] = {
|
||||
{AF_FORMAT_S8, SND_PCM_FORMAT_S8},
|
||||
{AF_FORMAT_U8, SND_PCM_FORMAT_U8},
|
||||
{AF_FORMAT_U16_LE, SND_PCM_FORMAT_U16_LE},
|
||||
{AF_FORMAT_U16_BE, SND_PCM_FORMAT_U16_BE},
|
||||
{AF_FORMAT_S16_LE, SND_PCM_FORMAT_S16_LE},
|
||||
{AF_FORMAT_S16_BE, SND_PCM_FORMAT_S16_BE},
|
||||
{AF_FORMAT_U32_LE, SND_PCM_FORMAT_U32_LE},
|
||||
{AF_FORMAT_U32_BE, SND_PCM_FORMAT_U32_BE},
|
||||
{AF_FORMAT_S32_LE, SND_PCM_FORMAT_S32_LE},
|
||||
{AF_FORMAT_S32_BE, SND_PCM_FORMAT_S32_BE},
|
||||
{AF_FORMAT_U24_LE, SND_PCM_FORMAT_U24_3LE},
|
||||
{AF_FORMAT_U24_BE, SND_PCM_FORMAT_U24_3BE},
|
||||
{AF_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE},
|
||||
{AF_FORMAT_S24_BE, SND_PCM_FORMAT_S24_3BE},
|
||||
{AF_FORMAT_FLOAT_LE, SND_PCM_FORMAT_FLOAT_LE},
|
||||
{AF_FORMAT_FLOAT_BE, SND_PCM_FORMAT_FLOAT_BE},
|
||||
{AF_FORMAT_AC3_LE, SND_PCM_FORMAT_S16_LE},
|
||||
{AF_FORMAT_AC3_BE, SND_PCM_FORMAT_S16_BE},
|
||||
{AF_FORMAT_IEC61937_LE, SND_PCM_FORMAT_S16_LE},
|
||||
{AF_FORMAT_IEC61937_BE, SND_PCM_FORMAT_S16_BE},
|
||||
{AF_FORMAT_MPEG2, SND_PCM_FORMAT_MPEG},
|
||||
{AF_FORMAT_U16, SND_PCM_FORMAT_U16},
|
||||
{AF_FORMAT_S16, SND_PCM_FORMAT_S16},
|
||||
{AF_FORMAT_U32, SND_PCM_FORMAT_U32},
|
||||
{AF_FORMAT_S32, SND_PCM_FORMAT_S32},
|
||||
{AF_FORMAT_U24,
|
||||
MP_SELECT_LE_BE(SND_PCM_FORMAT_U24_3LE, SND_PCM_FORMAT_U24_3BE)},
|
||||
{AF_FORMAT_S24,
|
||||
MP_SELECT_LE_BE(SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3BE)},
|
||||
{AF_FORMAT_FLOAT, SND_PCM_FORMAT_FLOAT},
|
||||
{AF_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN},
|
||||
};
|
||||
|
||||
@@ -409,7 +400,15 @@ static int init(struct ao *ao)
|
||||
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
|
||||
CHECK_ALSA_ERROR("Unable to get initial parameters");
|
||||
|
||||
p->alsa_fmt = find_alsa_format(ao->format);
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
if (ao->format == AF_FORMAT_S_MP3) {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_MPEG;
|
||||
} else {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
}
|
||||
} else {
|
||||
p->alsa_fmt = find_alsa_format(ao->format);
|
||||
}
|
||||
if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
ao->format = AF_FORMAT_S16;
|
||||
@@ -417,15 +416,12 @@ static int init(struct ao *ao)
|
||||
|
||||
err = snd_pcm_hw_params_test_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||
if (err < 0) {
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
CHECK_ALSA_ERROR("Unable to set IEC61937 format");
|
||||
MP_INFO(ao, "Format %s is not supported by hardware, trying default.\n",
|
||||
af_fmt_to_str(ao->format));
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16_LE;
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
ao->format = AF_FORMAT_AC3_LE;
|
||||
else if (AF_FORMAT_IS_IEC61937(ao->format))
|
||||
ao->format = AF_FORMAT_IEC61937_LE;
|
||||
else
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
p->alsa_fmt = SND_PCM_FORMAT_S16;
|
||||
ao->format = AF_FORMAT_S16;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
|
||||
@@ -675,7 +671,7 @@ static int get_space(struct ao *ao)
|
||||
unsigned space = snd_pcm_status_get_avail(status);
|
||||
if (space > p->buffersize) // Buffer underrun?
|
||||
space = p->buffersize;
|
||||
return space;
|
||||
return space / p->outburst * p->outburst;
|
||||
|
||||
alsa_error:
|
||||
return 0;
|
||||
|
||||
@@ -136,6 +136,10 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return get_volume(ao, arg);
|
||||
case AOCONTROL_SET_VOLUME:
|
||||
return set_volume(ao, arg);
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -149,9 +153,6 @@ static int init(struct ao *ao)
|
||||
|
||||
if (p->opt_list) ca_print_device_list(ao);
|
||||
|
||||
ao->per_application_mixer = true;
|
||||
ao->no_persistent_volume = true;
|
||||
|
||||
OSStatus err = ca_select_device(ao, p->opt_device_id, &p->device);
|
||||
CHECK_CA_ERROR("failed to select device");
|
||||
|
||||
|
||||
@@ -404,23 +404,15 @@ static int init(struct ao *ao)
|
||||
|
||||
if (p->opt_list) ca_print_device_list(ao);
|
||||
|
||||
*p = (struct priv) {
|
||||
.muted = false,
|
||||
.stream_asbd_changed = 0,
|
||||
.hog_pid = -1,
|
||||
.stream = 0,
|
||||
.stream_idx = -1,
|
||||
.changed_mixing = false,
|
||||
};
|
||||
|
||||
OSStatus err = ca_select_device(ao, p->opt_device_id, &p->device);
|
||||
CHECK_CA_ERROR("failed to select device");
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
bool supports_digital = false;
|
||||
/* Probe whether device support S/PDIF stream output if input is AC3 stream. */
|
||||
if (AF_FORMAT_IS_AC3(ao->format)) {
|
||||
/* Probe whether device support S/PDIF stream output if input is AC3 stream,
|
||||
* or anything else IEC61937-framed. */
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
if (ca_device_supports_digital(ao, p->device))
|
||||
supports_digital = true;
|
||||
}
|
||||
@@ -530,14 +522,7 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
|
||||
err = ca_enable_device_listener(p->device, changed);
|
||||
CHECK_CA_ERROR("cannot install format change listener during init");
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
if (!(p->stream_asdb.mFormatFlags & kAudioFormatFlagIsBigEndian))
|
||||
#else
|
||||
/* tell mplayer that we need a byteswap on AC3 streams, */
|
||||
if (p->stream_asbd.mFormatID & kAudioFormat60958AC3)
|
||||
ao->format = AF_FORMAT_AC3_LE;
|
||||
else if (p->stream_asbd.mFormatFlags & kAudioFormatFlagIsBigEndian)
|
||||
#endif
|
||||
if (p->stream_asbd.mFormatFlags & kAudioFormatFlagIsBigEndian)
|
||||
MP_WARN(ao, "stream has non-native byte order, output may fail\n");
|
||||
|
||||
ao->samplerate = p->stream_asbd.mSampleRate;
|
||||
@@ -676,6 +661,14 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
|
||||
.pause = audio_pause,
|
||||
.resume = audio_resume,
|
||||
.priv_size = sizeof(struct priv),
|
||||
.priv_defaults = &(const struct priv){
|
||||
.muted = false,
|
||||
.stream_asbd_changed = 0,
|
||||
.hog_pid = -1,
|
||||
.stream = 0,
|
||||
.stream_idx = -1,
|
||||
.changed_mixing = false,
|
||||
},
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INT("device_id", opt_device_id, 0, OPTDEF_INT(-1)),
|
||||
OPT_FLAG("list", opt_list, 0),
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "audio/out/ao_coreaudio_utils.h"
|
||||
#include "audio/out/ao_coreaudio_properties.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/endian.h"
|
||||
#include "audio/format.h"
|
||||
|
||||
void ca_print_device_list(struct ao *ao)
|
||||
@@ -129,20 +130,21 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
|
||||
void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd)
|
||||
{
|
||||
asbd->mSampleRate = ao->samplerate;
|
||||
asbd->mFormatID = AF_FORMAT_IS_AC3(ao->format) ?
|
||||
// Set "AC3" for other spdif formats too - unknown if that works.
|
||||
asbd->mFormatID = AF_FORMAT_IS_IEC61937(ao->format) ?
|
||||
kAudioFormat60958AC3 :
|
||||
kAudioFormatLinearPCM;
|
||||
asbd->mChannelsPerFrame = ao->channels.num;
|
||||
asbd->mBitsPerChannel = af_fmt2bits(ao->format);
|
||||
asbd->mFormatFlags = kAudioFormatFlagIsPacked;
|
||||
|
||||
if ((ao->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F)
|
||||
if ((ao->format & AF_FORMAT_TYPE_MASK) == AF_FORMAT_F)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsFloat;
|
||||
|
||||
if ((ao->format & AF_FORMAT_SIGN_MASK) == AF_FORMAT_SI)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsSignedInteger;
|
||||
|
||||
if ((ao->format & AF_FORMAT_END_MASK) == AF_FORMAT_BE)
|
||||
if (BYTE_ORDER == BIG_ENDIAN)
|
||||
asbd->mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
|
||||
asbd->mFramesPerPacket = 1;
|
||||
|
||||
+16
-13
@@ -98,6 +98,7 @@ struct priv {
|
||||
int outburst; ///play in multiple of chunks of this size
|
||||
|
||||
int cfg_device;
|
||||
int cfg_buffersize;
|
||||
};
|
||||
|
||||
static float get_delay(struct ao *ao);
|
||||
@@ -350,6 +351,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
IDirectSoundBuffer_SetVolume(p->hdsbuf, volume);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -370,7 +373,6 @@ static int init(struct ao *ao)
|
||||
if (!InitDirectSound(ao))
|
||||
return -1;
|
||||
|
||||
ao->no_persistent_volume = true;
|
||||
p->audio_volume = 100;
|
||||
|
||||
// ok, now create the buffers
|
||||
@@ -380,34 +382,33 @@ static int init(struct ao *ao)
|
||||
int format = af_fmt_from_planar(ao->format);
|
||||
int rate = ao->samplerate;
|
||||
|
||||
if (AF_FORMAT_IS_AC3(format))
|
||||
format = AF_FORMAT_AC3;
|
||||
else {
|
||||
if (!AF_FORMAT_IS_IEC61937(format)) {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
mp_chmap_sel_add_waveext(&sel);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
return -1;
|
||||
}
|
||||
switch (format) {
|
||||
case AF_FORMAT_AC3:
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S24:
|
||||
case AF_FORMAT_S16:
|
||||
case AF_FORMAT_U8:
|
||||
break;
|
||||
default:
|
||||
if (AF_FORMAT_IS_IEC61937(format))
|
||||
break;
|
||||
MP_VERBOSE(ao, "format %s not supported defaulting to Signed 16-bit Little-Endian\n",
|
||||
af_fmt_to_str(format));
|
||||
format = AF_FORMAT_S16_LE;
|
||||
format = AF_FORMAT_S16;
|
||||
}
|
||||
//set our audio parameters
|
||||
ao->samplerate = rate;
|
||||
ao->format = format;
|
||||
ao->bps = ao->channels.num * rate * af_fmt2bps(format);
|
||||
int buffersize = ao->bps * 0.100; // space for 100ms
|
||||
int buffersize = ao->bps * p->cfg_buffersize / 1000;
|
||||
MP_VERBOSE(ao, "Samplerate:%iHz Channels:%i Format:%s\n", rate,
|
||||
ao->channels.num, af_fmt_to_str(format));
|
||||
MP_VERBOSE(ao, "Buffersize:%d bytes (%d msec)\n",
|
||||
buffersize, buffersize / ao->bps * 1000);
|
||||
MP_VERBOSE(ao, "Buffersize:%d bytes (%f msec)\n",
|
||||
buffersize, buffersize * 1000.0 / ao->bps);
|
||||
|
||||
//fill waveformatex
|
||||
ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
@@ -415,7 +416,8 @@ static int init(struct ao *ao)
|
||||
? sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) : 0;
|
||||
wformat.Format.nChannels = ao->channels.num;
|
||||
wformat.Format.nSamplesPerSec = rate;
|
||||
if (AF_FORMAT_IS_AC3(format)) {
|
||||
if (AF_FORMAT_IS_IEC61937(format)) {
|
||||
// Whether it also works with e.g. DTS is unknown, but probably does.
|
||||
wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
|
||||
wformat.Format.wBitsPerSample = 16;
|
||||
wformat.Format.nBlockAlign = 4;
|
||||
@@ -583,7 +585,7 @@ static int get_space(struct ao *ao)
|
||||
int space = check_free_buffer_size(ao);
|
||||
if (space < p->min_free_space)
|
||||
return 0;
|
||||
return (space - p->min_free_space) / ao->sstride;
|
||||
return (space - p->min_free_space) / p->outburst * p->outburst / ao->sstride;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -636,6 +638,7 @@ const struct ao_driver audio_out_dsound = {
|
||||
.priv_size = sizeof(struct priv),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_INT("device", cfg_device, 0),
|
||||
OPT_INTRANGE("buffersize", cfg_buffersize, 0, 1, 10000, OPTDEF_INT(200)),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/audioconvert.h>
|
||||
|
||||
#include "compat/libav.h"
|
||||
#include "config.h"
|
||||
#include "options/options.h"
|
||||
#include "common/common.h"
|
||||
|
||||
+2
-1
@@ -148,7 +148,8 @@ static int get_space(struct ao *ao)
|
||||
struct priv *priv = ao->priv;
|
||||
|
||||
drain(ao);
|
||||
return priv->buffersize - priv->latency - priv->buffered;
|
||||
int samples = priv->buffersize - priv->latency - priv->buffered;
|
||||
return samples / priv->outburst * priv->outburst;
|
||||
}
|
||||
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
@@ -76,6 +76,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
vol->left = vol->right = volume * 100;
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -132,7 +134,6 @@ static int init(struct ao *ao)
|
||||
return -1;
|
||||
}
|
||||
ao_data = ao;
|
||||
ao->no_persistent_volume = true;
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (i = 0; speaker_pos[i].id != -1; i++)
|
||||
mp_chmap_sel_add_speaker(&sel, speaker_pos[i].id);
|
||||
|
||||
+320
-296
@@ -31,6 +31,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
@@ -38,6 +39,8 @@
|
||||
#include "config.h"
|
||||
#include "options/options.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#if HAVE_SYS_SOUNDCARD_H
|
||||
#include <sys/soundcard.h>
|
||||
@@ -52,14 +55,20 @@
|
||||
#include "ao.h"
|
||||
#include "internal.h"
|
||||
|
||||
// Define to 0 if the device must be reopened to reset it (stop all playback,
|
||||
// clear the buffer), and the device should be closed when unused.
|
||||
// Define to 1 if SNDCTL_DSP_RESET should be used to reset without close.
|
||||
#define KEEP_DEVICE (defined(SNDCTL_DSP_RESET) && !defined(__NetBSD__))
|
||||
|
||||
struct priv {
|
||||
int audio_fd;
|
||||
int prepause_space;
|
||||
int prepause_samples;
|
||||
int oss_mixer_channel;
|
||||
audio_buf_info zz;
|
||||
int audio_delay_method;
|
||||
int buffersize;
|
||||
int outburst;
|
||||
bool device_failed;
|
||||
double audio_end;
|
||||
|
||||
char *dsp;
|
||||
char *oss_mixer_device;
|
||||
@@ -81,53 +90,60 @@ static const struct mp_chmap oss_layouts[MP_NUM_CHANNELS + 1] = {
|
||||
MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), // 7.1
|
||||
};
|
||||
|
||||
#if !defined(AFMT_S16_NE) && defined(AFMT_S16_LE) && defined(AFMT_S16_BE)
|
||||
#define AFMT_S16_NE MP_SELECT_LE_BE(AFMT_S16_LE, AFMT_S16_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_U16_NE) && defined(AFMT_U16_LE) && defined(AFMT_U16_BE)
|
||||
#define AFMT_U16_NE MP_SELECT_LE_BE(AFMT_U16_LE, AFMT_U16_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_U24_NE) && defined(AFMT_U24_LE) && defined(AFMT_U24_BE)
|
||||
#define AFMT_U24_NE MP_SELECT_LE_BE(AFMT_U24_LE, AFMT_U24_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_S24_NE) && defined(AFMT_S24_LE) && defined(AFMT_S24_BE)
|
||||
#define AFMT_S24_NE MP_SELECT_LE_BE(AFMT_S24_LE, AFMT_S24_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_U32_NE) && defined(AFMT_U32_LE) && defined(AFMT_U32_BE)
|
||||
#define AFMT_U32MP_SELECT_LE_BE(AFMT_U32_LE, AFMT_U32_BE)
|
||||
#endif
|
||||
|
||||
#if !defined(AFMT_S32_NE) && defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
|
||||
#define AFMT_S32MP_SELECT_LE_BE(AFMT_S32_LE, AFMT_S32_BE)
|
||||
#endif
|
||||
|
||||
static const int format_table[][2] = {
|
||||
{AFMT_U8, AF_FORMAT_U8},
|
||||
{AFMT_S8, AF_FORMAT_S8},
|
||||
{AFMT_U16_LE, AF_FORMAT_U16_LE},
|
||||
{AFMT_U16_BE, AF_FORMAT_U16_BE},
|
||||
{AFMT_S16_LE, AF_FORMAT_S16_LE},
|
||||
{AFMT_S16_BE, AF_FORMAT_S16_BE},
|
||||
#ifdef AFMT_S24_PACKED
|
||||
{AFMT_S24_PACKED, AF_FORMAT_S24_LE},
|
||||
{AFMT_U16_NE, AF_FORMAT_U16},
|
||||
{AFMT_S16_NE, AF_FORMAT_S16},
|
||||
#ifdef AFMT_U24_NE
|
||||
{AFMT_U24_NE, AF_FORMAT_U24},
|
||||
#endif
|
||||
#ifdef AFMT_U24_LE
|
||||
{AFMT_U24_LE, AF_FORMAT_U24_LE},
|
||||
#ifdef AFMT_S24_NE
|
||||
{AFMT_S24_NE, AF_FORMAT_S24},
|
||||
#endif
|
||||
#ifdef AFMT_U24_BE
|
||||
{AFMT_U24_BE, AF_FORMAT_U24_BE},
|
||||
#ifdef AFMT_U32_NE
|
||||
{AFMT_U32_NE, AF_FORMAT_U32},
|
||||
#endif
|
||||
#ifdef AFMT_S24_LE
|
||||
{AFMT_S24_LE, AF_FORMAT_S24_LE},
|
||||
#endif
|
||||
#ifdef AFMT_S24_BE
|
||||
{AFMT_S24_BE, AF_FORMAT_S24_BE},
|
||||
#endif
|
||||
#ifdef AFMT_U32_LE
|
||||
{AFMT_U32_LE, AF_FORMAT_U32_LE},
|
||||
#endif
|
||||
#ifdef AFMT_U32_BE
|
||||
{AFMT_U32_BE, AF_FORMAT_U32_BE},
|
||||
#endif
|
||||
#ifdef AFMT_S32_LE
|
||||
{AFMT_S32_LE, AF_FORMAT_S32_LE},
|
||||
#endif
|
||||
#ifdef AFMT_S32_BE
|
||||
{AFMT_S32_BE, AF_FORMAT_S32_BE},
|
||||
#ifdef AFMT_S32_NE
|
||||
{AFMT_S32_NE, AF_FORMAT_S32},
|
||||
#endif
|
||||
#ifdef AFMT_FLOAT
|
||||
{AFMT_FLOAT, AF_FORMAT_FLOAT},
|
||||
#endif
|
||||
// SPECIALS
|
||||
#ifdef AFMT_MPEG
|
||||
{AFMT_MPEG, AF_FORMAT_MPEG2},
|
||||
#endif
|
||||
#ifdef AFMT_AC3
|
||||
{AFMT_AC3, AF_FORMAT_AC3},
|
||||
{AFMT_MPEG, AF_FORMAT_S_MP3},
|
||||
#endif
|
||||
{-1, -1}
|
||||
};
|
||||
|
||||
#ifndef AFMT_AC3
|
||||
#define AFMT_AC3 -1
|
||||
#endif
|
||||
|
||||
static int format2oss(int format)
|
||||
{
|
||||
for (int n = 0; format_table[n][0] != -1; n++) {
|
||||
@@ -143,7 +159,7 @@ static int oss2format(int format)
|
||||
if (format_table[n][0] == format)
|
||||
return format_table[n][1];
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -178,8 +194,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
struct priv *p = ao->priv;
|
||||
switch (cmd) {
|
||||
case AOCONTROL_GET_VOLUME:
|
||||
case AOCONTROL_SET_VOLUME:
|
||||
{
|
||||
case AOCONTROL_SET_VOLUME: {
|
||||
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
||||
int fd, v, devs;
|
||||
|
||||
@@ -189,7 +204,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return CONTROL_OK;
|
||||
#endif
|
||||
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
if (AF_FORMAT_IS_SPECIAL(ao->format))
|
||||
return CONTROL_TRUE;
|
||||
|
||||
if ((fd = open(p->oss_mixer_device, O_RDONLY)) != -1) {
|
||||
@@ -210,22 +225,203 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
close(fd);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
}
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
#ifdef SNDCTL_DSP_GETPLAYVOL
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
#endif
|
||||
}
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
||||
// 1: ok, 0: not writable, -1: error
|
||||
static int device_writable(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
struct pollfd fd = {.fd = p->audio_fd, .events = POLLOUT};
|
||||
return poll(&fd, 1, 0);
|
||||
}
|
||||
|
||||
static void close_device(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->device_failed = false;
|
||||
if (p->audio_fd == -1)
|
||||
return;
|
||||
#if defined(SNDCTL_DSP_RESET)
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#endif
|
||||
close(p->audio_fd);
|
||||
p->audio_fd = -1;
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
close_device(ao);
|
||||
}
|
||||
|
||||
static bool try_format(struct ao *ao, int *format)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
int oss_format = format2oss(*format);
|
||||
if (oss_format == -1 && AF_FORMAT_IS_IEC61937(*format))
|
||||
oss_format = AFMT_AC3;
|
||||
|
||||
if (oss_format == -1) {
|
||||
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
|
||||
af_fmt_to_str(*format));
|
||||
*format = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
int actual_format = oss_format;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &actual_format) < 0)
|
||||
actual_format = -1;
|
||||
|
||||
if (actual_format == oss_format)
|
||||
return true;
|
||||
|
||||
MP_WARN(ao, "Can't set audio device to %s output.\n", af_fmt_to_str(*format));
|
||||
*format = oss2format(actual_format);
|
||||
if (actual_format != -1 && !*format)
|
||||
MP_ERR(ao, "Unknown/Unsupported OSS format: 0x%x.\n", actual_format);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int reopen_device(struct ao *ao, bool allow_format_changes)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
int samplerate = ao->samplerate;
|
||||
int format = ao->format;
|
||||
struct mp_chmap channels = ao->channels;
|
||||
|
||||
#ifdef __linux__
|
||||
p->audio_fd = open(p->dsp, O_WRONLY | O_NONBLOCK);
|
||||
#else
|
||||
p->audio_fd = open(p->dsp, O_WRONLY);
|
||||
#endif
|
||||
if (p->audio_fd < 0) {
|
||||
MP_ERR(ao, "Can't open audio device %s: %s\n", p->dsp, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/* Remove the non-blocking flag */
|
||||
if (fcntl(p->audio_fd, F_SETFL, 0) < 0) {
|
||||
MP_ERR(ao, "Can't make file descriptor blocking: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FD_CLOEXEC) && defined(F_SETFD)
|
||||
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(format)) {
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &samplerate);
|
||||
// Probably could be fixed by setting number of channels; needs testing.
|
||||
if (channels.num != 2) {
|
||||
MP_ERR(ao, "Format %s not implemented.\n", af_fmt_to_str(format));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
int try_formats[] = {format, AF_FORMAT_S32, AF_FORMAT_S24, AF_FORMAT_S16, 0};
|
||||
for (int n = 0; try_formats[n]; n++) {
|
||||
format = try_formats[n];
|
||||
if (try_format(ao, &format))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!format) {
|
||||
MP_ERR(ao, "Can't set sample format.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(format));
|
||||
|
||||
if (!AF_FORMAT_IS_IEC61937(format)) {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (int n = 0; n < MP_NUM_CHANNELS + 1; n++)
|
||||
mp_chmap_sel_add_map(&sel, &oss_layouts[n]);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &channels))
|
||||
goto fail;
|
||||
int reqchannels = channels.num;
|
||||
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
|
||||
if (reqchannels > 2) {
|
||||
int nchannels = reqchannels;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 ||
|
||||
nchannels != reqchannels)
|
||||
{
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
int c = reqchannels - 1;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
goto fail;
|
||||
}
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &channels, c + 1))
|
||||
goto fail;
|
||||
}
|
||||
MP_VERBOSE(ao, "using %d channels (requested: %d)\n",
|
||||
channels.num, reqchannels);
|
||||
// set rate
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &samplerate);
|
||||
MP_VERBOSE(ao, "using %d Hz samplerate\n", samplerate);
|
||||
}
|
||||
|
||||
audio_buf_info zz = {0};
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &zz) == -1) {
|
||||
int r = 0;
|
||||
MP_WARN(ao, "driver doesn't support SNDCTL_DSP_GETOSPACE\n");
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETBLKSIZE, &r) == -1)
|
||||
MP_VERBOSE(ao, "%d bytes/frag (config.h)\n", p->outburst);
|
||||
else {
|
||||
p->outburst = r;
|
||||
MP_VERBOSE(ao, "%d bytes/frag (GETBLKSIZE)\n", p->outburst);
|
||||
}
|
||||
} else {
|
||||
MP_VERBOSE(ao, "frags: %3d/%d (%d bytes/frag) free: %6d\n",
|
||||
zz.fragments, zz.fragstotal, zz.fragsize, zz.bytes);
|
||||
p->buffersize = zz.bytes;
|
||||
p->outburst = zz.fragsize;
|
||||
}
|
||||
|
||||
if (allow_format_changes) {
|
||||
ao->format = format;
|
||||
ao->samplerate = samplerate;
|
||||
ao->channels = channels;
|
||||
} else {
|
||||
if (format != ao->format || samplerate != ao->samplerate ||
|
||||
!mp_chmap_equals(&channels, &ao->channels))
|
||||
{
|
||||
MP_ERR(ao, "Could not reselect previous audio format.\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
p->outburst -= p->outburst % (channels.num * af_fmt2bps(format)); // round down
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close_device(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// open & setup audio device
|
||||
// return: 0=success -1=fail
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int oss_format;
|
||||
|
||||
#ifdef SNDCTL_DSP_GETPLAYVOL
|
||||
ao->no_persistent_volume = true;
|
||||
#endif
|
||||
|
||||
const char *mchan = NULL;
|
||||
if (p->cfg_oss_mixer_channel && p->cfg_oss_mixer_channel[0])
|
||||
@@ -266,178 +462,36 @@ static int init(struct ao *ao)
|
||||
MP_VERBOSE(ao, "using '%s' mixer device\n", p->oss_mixer_device);
|
||||
MP_VERBOSE(ao, "using '%s' mixer device\n", mixer_channels[p->oss_mixer_channel]);
|
||||
|
||||
#ifdef __linux__
|
||||
p->audio_fd = open(p->dsp, O_WRONLY | O_NONBLOCK);
|
||||
#else
|
||||
p->audio_fd = open(p->dsp, O_WRONLY);
|
||||
#endif
|
||||
if (p->audio_fd < 0) {
|
||||
MP_ERR(ao, "Can't open audio device %s: %s\n", p->dsp, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/* Remove the non-blocking flag */
|
||||
if (fcntl(p->audio_fd, F_SETFL, 0) < 0) {
|
||||
MP_ERR(ao, "Can't make file descriptor blocking: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FD_CLOEXEC) && defined(F_SETFD)
|
||||
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
if (AF_FORMAT_IS_AC3(ao->format)) {
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
}
|
||||
|
||||
ac3_retry:
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
ao->format = AF_FORMAT_AC3;
|
||||
oss_format = format2oss(ao->format);
|
||||
if (oss_format == -1) {
|
||||
MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n",
|
||||
af_fmt_to_str(ao->format));
|
||||
#if defined(AFMT_S32_LE) && defined(AFMT_S32_BE)
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
oss_format = AFMT_S32_BE;
|
||||
#else
|
||||
oss_format = AFMT_S32_LE;
|
||||
#endif
|
||||
ao->format = AF_FORMAT_S32;
|
||||
#elif defined(AFMT_S24_LE) && defined(AFMT_S24_BE)
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
oss_format = AFMT_S24_BE;
|
||||
#else
|
||||
oss_format = AFMT_S24_LE;
|
||||
#endif
|
||||
ao->format = AF_FORMAT_S24;
|
||||
#else
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
oss_format = AFMT_S16_BE;
|
||||
#else
|
||||
oss_format = AFMT_S16_LE;
|
||||
#endif
|
||||
ao->format = AF_FORMAT_S16;
|
||||
#endif
|
||||
}
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format) < 0 ||
|
||||
oss_format != format2oss(ao->format))
|
||||
{
|
||||
MP_WARN(ao, "Can't set audio device %s to %s output, trying %s...\n",
|
||||
p->dsp, af_fmt_to_str(ao->format),
|
||||
af_fmt_to_str(AF_FORMAT_S16));
|
||||
ao->format = AF_FORMAT_S16;
|
||||
goto ac3_retry;
|
||||
}
|
||||
|
||||
ao->format = oss2format(oss_format);
|
||||
if (ao->format == -1) {
|
||||
MP_ERR(ao, "Unknown/Unsupported OSS format: %x.\n", oss_format);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(ao->format));
|
||||
|
||||
if (!AF_FORMAT_IS_AC3(ao->format)) {
|
||||
struct mp_chmap_sel sel = {0};
|
||||
for (int n = 0; n < MP_NUM_CHANNELS + 1; n++)
|
||||
mp_chmap_sel_add_map(&sel, &oss_layouts[n]);
|
||||
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
|
||||
return -1;
|
||||
int reqchannels = ao->channels.num;
|
||||
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
|
||||
if (reqchannels > 2) {
|
||||
int nchannels = reqchannels;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1 ||
|
||||
nchannels != reqchannels)
|
||||
{
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
int c = reqchannels - 1;
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
|
||||
MP_ERR(ao, "Failed to set audio device to %d channels.\n",
|
||||
reqchannels);
|
||||
return -1;
|
||||
}
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, c + 1))
|
||||
return -1;
|
||||
}
|
||||
MP_VERBOSE(ao, "using %d channels (requested: %d)\n",
|
||||
ao->channels.num, reqchannels);
|
||||
// set rate
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
MP_VERBOSE(ao, "using %d Hz samplerate\n", ao->samplerate);
|
||||
}
|
||||
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) == -1) {
|
||||
int r = 0;
|
||||
MP_WARN(ao, "driver doesn't support SNDCTL_DSP_GETOSPACE\n");
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETBLKSIZE, &r) == -1)
|
||||
MP_VERBOSE(ao, "%d bytes/frag (config.h)\n", p->outburst);
|
||||
else {
|
||||
p->outburst = r;
|
||||
MP_VERBOSE(ao, "%d bytes/frag (GETBLKSIZE)\n", p->outburst);
|
||||
}
|
||||
} else {
|
||||
MP_VERBOSE(ao, "frags: %3d/%d (%d bytes/frag) free: %6d\n",
|
||||
p->zz.fragments, p->zz.fragstotal, p->zz.fragsize, p->zz.bytes);
|
||||
p->buffersize = p->zz.bytes;
|
||||
p->outburst = p->zz.fragsize;
|
||||
}
|
||||
if (reopen_device(ao, true) < 0)
|
||||
goto fail;
|
||||
|
||||
if (p->buffersize == -1) {
|
||||
// Measuring buffer size:
|
||||
void *data;
|
||||
void *data = malloc(p->outburst);
|
||||
if (!data) {
|
||||
MP_ERR(ao, "Out of memory, or broken outburst size.\n");
|
||||
goto fail;
|
||||
}
|
||||
p->buffersize = 0;
|
||||
#if HAVE_AUDIO_SELECT
|
||||
data = malloc(p->outburst);
|
||||
memset(data, 0, p->outburst);
|
||||
while (p->buffersize < 0x40000) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(p->audio_fd, &rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
if (!select(p->audio_fd + 1, NULL, &rfds, NULL, &tv))
|
||||
break;
|
||||
while (p->buffersize < 0x40000 && device_writable(ao) > 0) {
|
||||
write(p->audio_fd, data, p->outburst);
|
||||
p->buffersize += p->outburst;
|
||||
}
|
||||
free(data);
|
||||
if (p->buffersize == 0) {
|
||||
MP_ERR(ao, "*** Your audio driver DOES NOT support select() ***\n");
|
||||
MP_ERR(ao, "Recompile mpv with #define HAVE_AUDIO_SELECT 0 in config.h!\n");
|
||||
return -1;
|
||||
MP_ERR(ao, "Your OSS audio driver DOES NOT support poll().\n");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ao->bps = ao->channels.num * af_fmt2bps(ao->format);
|
||||
p->outburst -= p->outburst % ao->bps; // round down
|
||||
ao->bps *= ao->samplerate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
if (p->audio_fd == -1)
|
||||
return;
|
||||
#ifdef SNDCTL_DSP_RESET
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#endif
|
||||
close(p->audio_fd);
|
||||
p->audio_fd = -1;
|
||||
fail:
|
||||
uninit(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void drain(struct ao *ao)
|
||||
@@ -450,104 +504,39 @@ static void drain(struct ao *ao)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SNDCTL_DSP_RESET
|
||||
static void close_device(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
close(p->audio_fd);
|
||||
p->audio_fd = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// stop playing and empty buffers (for seeking/pause)
|
||||
static void reset(struct ao *ao)
|
||||
{
|
||||
#if KEEP_DEVICE
|
||||
struct priv *p = ao->priv;
|
||||
int oss_format;
|
||||
#ifdef SNDCTL_DSP_RESET
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#else
|
||||
close_device(ao);
|
||||
p->audio_fd = open(p->dsp, O_WRONLY);
|
||||
if (p->audio_fd < 0) {
|
||||
MP_ERR(ao, "Fatal error: *** CANNOT "
|
||||
"RE-OPEN / RESET AUDIO DEVICE *** %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(FD_CLOEXEC) && defined(F_SETFD)
|
||||
fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
oss_format = format2oss(ao->format);
|
||||
if (AF_FORMAT_IS_AC3(ao->format))
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
|
||||
if (!AF_FORMAT_IS_AC3(ao->format)) {
|
||||
int c = ao->channels.num;
|
||||
if (ao->channels.num > 2)
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &c);
|
||||
else {
|
||||
c--;
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c);
|
||||
}
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
|
||||
}
|
||||
}
|
||||
|
||||
// return: how many bytes can be played without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int playsize = p->outburst;
|
||||
|
||||
#ifdef SNDCTL_DSP_GETOSPACE
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) {
|
||||
// calculate exact buffer space:
|
||||
playsize = p->zz.fragments * p->zz.fragsize;
|
||||
return playsize / ao->sstride;
|
||||
}
|
||||
#endif
|
||||
|
||||
// check buffer
|
||||
#if HAVE_AUDIO_SELECT
|
||||
{
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(p->audio_fd, &rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
if (!select(p->audio_fd + 1, NULL, &rfds, NULL, &tv))
|
||||
return 0; // not block!
|
||||
}
|
||||
#endif
|
||||
|
||||
return p->outburst / ao->sstride;
|
||||
}
|
||||
|
||||
// stop playing, keep buffers (for pause)
|
||||
static void audio_pause(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->prepause_space = get_space(ao) * ao->sstride;
|
||||
#ifdef SNDCTL_DSP_RESET
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#else
|
||||
close_device(ao);
|
||||
#endif
|
||||
}
|
||||
|
||||
// plays 'len' bytes of 'data'
|
||||
// plays 'len' samples of 'data'
|
||||
// it should round it down to outburst*n
|
||||
// return: number of bytes played
|
||||
// return: number of samples played
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int len = samples * ao->sstride;
|
||||
if (len == 0)
|
||||
return len;
|
||||
|
||||
if (p->audio_fd < 0 && !p->device_failed && reopen_device(ao, false) < 0)
|
||||
MP_ERR(ao, "Fatal error: *** CANNOT RE-OPEN / RESET AUDIO DEVICE ***\n");
|
||||
if (p->audio_fd < 0) {
|
||||
// Let playback continue normally, even with a closed device.
|
||||
p->device_failed = true;
|
||||
double now = mp_time_sec();
|
||||
if (p->audio_end < now)
|
||||
p->audio_end = now;
|
||||
p->audio_end += samples / (double)ao->samplerate;
|
||||
return samples;
|
||||
}
|
||||
|
||||
if (len > p->outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
|
||||
len /= p->outburst;
|
||||
len *= p->outburst;
|
||||
@@ -556,22 +545,16 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
return len / ao->sstride;
|
||||
}
|
||||
|
||||
// resume playing, after audio_pause()
|
||||
static void audio_resume(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
#ifndef SNDCTL_DSP_RESET
|
||||
reset(ao);
|
||||
#endif
|
||||
int fillframes = get_space(ao) - p->prepause_space / ao->sstride;
|
||||
if (fillframes > 0)
|
||||
ao_play_silence(ao, fillframes);
|
||||
}
|
||||
|
||||
// return: delay in seconds between first and last sample in buffer
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
if (p->audio_fd < 0) {
|
||||
double rest = p->audio_end - mp_time_sec();
|
||||
if (rest > 0)
|
||||
return rest;
|
||||
return 0;
|
||||
}
|
||||
/* Calculate how many bytes/second is sent out */
|
||||
if (p->audio_delay_method == 2) {
|
||||
#ifdef SNDCTL_DSP_GETODELAY
|
||||
@@ -582,16 +565,57 @@ static float get_delay(struct ao *ao)
|
||||
p->audio_delay_method = 1; // fallback if not supported
|
||||
}
|
||||
if (p->audio_delay_method == 1) {
|
||||
// SNDCTL_DSP_GETOSPACE
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) != -1) {
|
||||
return ((float)(p->buffersize -
|
||||
p->zz.bytes)) / (float)ao->bps;
|
||||
audio_buf_info zz = {0};
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &zz) != -1) {
|
||||
return ((float)(p->buffersize - zz.bytes)) / (float)ao->bps;
|
||||
}
|
||||
p->audio_delay_method = 0; // fallback if not supported
|
||||
}
|
||||
return ((float)p->buffersize) / (float)ao->bps;
|
||||
}
|
||||
|
||||
|
||||
// return: how many samples can be played without blocking
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
audio_buf_info zz = {0};
|
||||
if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &zz) != -1) {
|
||||
// calculate exact buffer space:
|
||||
return zz.fragments * zz.fragsize / ao->sstride;
|
||||
}
|
||||
|
||||
if (p->audio_fd < 0 && p->device_failed && get_delay(ao) > 0.2)
|
||||
return 0;
|
||||
|
||||
if (p->audio_fd < 0 || device_writable(ao) > 0)
|
||||
return p->outburst / ao->sstride;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// stop playing, keep buffers (for pause)
|
||||
static void audio_pause(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->prepause_samples = get_delay(ao) * ao->samplerate;
|
||||
#if KEEP_DEVICE
|
||||
ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL);
|
||||
#else
|
||||
close_device(ao);
|
||||
#endif
|
||||
}
|
||||
|
||||
// resume playing, after audio_pause()
|
||||
static void audio_resume(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
p->audio_end = 0;
|
||||
if (p->prepause_samples > 0)
|
||||
ao_play_silence(ao, p->prepause_samples);
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
const struct ao_driver audio_out_oss = {
|
||||
|
||||
+16
-9
@@ -35,6 +35,7 @@
|
||||
#include "ao.h"
|
||||
#include "internal.h"
|
||||
#include "common/msg.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// for GetFileType to detect pipes
|
||||
@@ -72,8 +73,7 @@ static void fput32le(uint32_t val, FILE *fp)
|
||||
static void write_wave_header(struct ao *ao, FILE *fp, uint64_t data_length)
|
||||
{
|
||||
bool use_waveex = true;
|
||||
uint16_t fmt = ao->format == AF_FORMAT_FLOAT_LE ?
|
||||
WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
uint16_t fmt = ao->format == AF_FORMAT_FLOAT ? WAV_ID_FLOAT_PCM : WAV_ID_PCM;
|
||||
uint32_t fmt_chunk_size = use_waveex ? 40 : 16;
|
||||
int bits = af_fmt2bits(ao->format);
|
||||
|
||||
@@ -124,16 +124,23 @@ static int init(struct ao *ao)
|
||||
if (priv->waveheader) {
|
||||
// WAV files must have one of the following formats
|
||||
|
||||
// And they don't work in big endian; fixing it would be simple, but
|
||||
// nobody cares.
|
||||
if (BYTE_ORDER == BIG_ENDIAN) {
|
||||
MP_FATAL(ao, "Not supported on big endian.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ao->format) {
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
case AF_FORMAT_AC3_LE:
|
||||
case AF_FORMAT_S16:
|
||||
case AF_FORMAT_S24:
|
||||
case AF_FORMAT_S32:
|
||||
case AF_FORMAT_FLOAT:
|
||||
break;
|
||||
default:
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
if (!AF_FORMAT_IS_IEC61937(ao->format))
|
||||
ao->format = AF_FORMAT_S16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -203,7 +210,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
fwrite(data[0], len, 1, priv->fp);
|
||||
priv->data_length += len;
|
||||
return len / ao->sstride;
|
||||
return samples;
|
||||
}
|
||||
|
||||
#define OPT_BASE_STRUCT struct priv
|
||||
|
||||
+76
-34
@@ -169,16 +169,31 @@ static const struct format_map {
|
||||
int mp_format;
|
||||
pa_sample_format_t pa_format;
|
||||
} format_maps[] = {
|
||||
{AF_FORMAT_S16_LE, PA_SAMPLE_S16LE},
|
||||
{AF_FORMAT_S16_BE, PA_SAMPLE_S16BE},
|
||||
{AF_FORMAT_S32_LE, PA_SAMPLE_S32LE},
|
||||
{AF_FORMAT_S32_BE, PA_SAMPLE_S32BE},
|
||||
{AF_FORMAT_FLOAT_LE, PA_SAMPLE_FLOAT32LE},
|
||||
{AF_FORMAT_FLOAT_BE, PA_SAMPLE_FLOAT32BE},
|
||||
{AF_FORMAT_S16, PA_SAMPLE_S16NE},
|
||||
{AF_FORMAT_S32, PA_SAMPLE_S32NE},
|
||||
{AF_FORMAT_FLOAT, PA_SAMPLE_FLOAT32NE},
|
||||
{AF_FORMAT_U8, PA_SAMPLE_U8},
|
||||
{AF_FORMAT_UNKNOWN, 0}
|
||||
};
|
||||
|
||||
static pa_encoding_t map_digital_format(int format)
|
||||
{
|
||||
switch (format) {
|
||||
case AF_FORMAT_S_AC3: return PA_ENCODING_AC3_IEC61937;
|
||||
case AF_FORMAT_S_EAC3: return PA_ENCODING_EAC3_IEC61937;
|
||||
case AF_FORMAT_S_MP3: return PA_ENCODING_MPEG_IEC61937;
|
||||
case AF_FORMAT_S_DTS:
|
||||
case AF_FORMAT_S_DTSHD: return PA_ENCODING_DTS_IEC61937;
|
||||
#ifdef PA_ENCODING_MPEG2_AAC_IEC61937
|
||||
case AF_FORMAT_S_AAC: return PA_ENCODING_MPEG2_AAC_IEC61937;
|
||||
#endif
|
||||
default:
|
||||
if (AF_FORMAT_IS_IEC61937(format))
|
||||
return PA_ENCODING_ANY;
|
||||
return PA_ENCODING_PCM;
|
||||
}
|
||||
}
|
||||
|
||||
static const int speaker_map[][2] = {
|
||||
{PA_CHANNEL_POSITION_FRONT_LEFT, MP_SPEAKER_ID_FL},
|
||||
{PA_CHANNEL_POSITION_FRONT_RIGHT, MP_SPEAKER_ID_FR},
|
||||
@@ -274,9 +289,9 @@ static void uninit(struct ao *ao)
|
||||
|
||||
static int init(struct ao *ao)
|
||||
{
|
||||
struct pa_sample_spec ss;
|
||||
struct pa_channel_map map;
|
||||
pa_proplist *proplist = NULL;
|
||||
pa_format_info *format = NULL;
|
||||
struct priv *priv = ao->priv;
|
||||
char *host = priv->cfg_host && priv->cfg_host[0] ? priv->cfg_host : NULL;
|
||||
char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : NULL;
|
||||
@@ -284,8 +299,6 @@ static int init(struct ao *ao)
|
||||
pthread_mutex_init(&priv->wakeup_lock, NULL);
|
||||
pthread_cond_init(&priv->wakeup, NULL);
|
||||
|
||||
ao->per_application_mixer = true;
|
||||
|
||||
if (!(priv->mainloop = pa_threaded_mainloop_new())) {
|
||||
MP_ERR(ao, "Failed to allocate main loop\n");
|
||||
goto fail;
|
||||
@@ -297,6 +310,12 @@ static int init(struct ao *ao)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
MP_VERBOSE(ao, "Library version: %s\n", pa_get_library_version());
|
||||
MP_VERBOSE(ao, "Proto: %lu\n",
|
||||
(long)pa_context_get_protocol_version(priv->context));
|
||||
MP_VERBOSE(ao, "Server proto: %lu\n",
|
||||
(long)pa_context_get_server_protocol_version(priv->context));
|
||||
|
||||
pa_context_set_state_callback(priv->context, context_state_cb, ao);
|
||||
|
||||
if (pa_context_connect(priv->context, host, 0, NULL) < 0)
|
||||
@@ -313,26 +332,26 @@ static int init(struct ao *ao)
|
||||
if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
|
||||
goto unlock_and_fail;
|
||||
|
||||
ss.channels = ao->channels.num;
|
||||
ss.rate = ao->samplerate;
|
||||
if (!(format = pa_format_info_new()))
|
||||
goto unlock_and_fail;
|
||||
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
MP_VERBOSE(ao, "Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
fmt_map++;
|
||||
}
|
||||
ao->format = fmt_map->mp_format;
|
||||
ss.format = fmt_map->pa_format;
|
||||
format->encoding = map_digital_format(ao->format);
|
||||
if (format->encoding == PA_ENCODING_PCM) {
|
||||
const struct format_map *fmt_map = format_maps;
|
||||
|
||||
if (!pa_sample_spec_valid(&ss)) {
|
||||
MP_ERR(ao, "Invalid sample spec\n");
|
||||
goto unlock_and_fail;
|
||||
while (fmt_map->mp_format != ao->format) {
|
||||
if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
|
||||
MP_VERBOSE(ao, "Unsupported format, using default\n");
|
||||
fmt_map = format_maps;
|
||||
break;
|
||||
}
|
||||
fmt_map++;
|
||||
}
|
||||
ao->format = fmt_map->mp_format;
|
||||
|
||||
pa_format_info_set_sample_format(format, fmt_map->pa_format);
|
||||
}
|
||||
|
||||
if (!select_chmap(ao, &map))
|
||||
@@ -346,9 +365,17 @@ static int init(struct ao *ao)
|
||||
(void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ICON_NAME,
|
||||
PULSE_CLIENT_NAME);
|
||||
|
||||
if (!(priv->stream = pa_stream_new_with_proplist(priv->context,
|
||||
"audio stream", &ss,
|
||||
&map, proplist)))
|
||||
pa_format_info_set_rate(format, ao->samplerate);
|
||||
pa_format_info_set_channels(format, ao->channels.num);
|
||||
pa_format_info_set_channel_map(format, &map);
|
||||
|
||||
if (!pa_format_info_valid(format)) {
|
||||
MP_ERR(ao, "Invalid audio format\n");
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
if (!(priv->stream = pa_stream_new_extended(priv->context, "audio stream",
|
||||
&format, 1, proplist)))
|
||||
goto unlock_and_fail;
|
||||
|
||||
pa_proplist_free(proplist);
|
||||
@@ -358,10 +385,11 @@ static int init(struct ao *ao)
|
||||
pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
|
||||
pa_stream_set_latency_update_callback(priv->stream,
|
||||
stream_latency_update_cb, ao);
|
||||
int buf_size = af_fmt_seconds_to_bytes(ao->format, priv->cfg_buffer / 1000.0,
|
||||
ao->channels.num, ao->samplerate);
|
||||
pa_buffer_attr bufattr = {
|
||||
.maxlength = -1,
|
||||
.tlength = priv->cfg_buffer > 0 ?
|
||||
pa_usec_to_bytes(priv->cfg_buffer * 1000, &ss) : (uint32_t)-1,
|
||||
.tlength = buf_size > 0 ? buf_size : (uint32_t)-1,
|
||||
.prebuf = -1,
|
||||
.minreq = -1,
|
||||
.fragsize = -1,
|
||||
@@ -376,10 +404,19 @@ static int init(struct ao *ao)
|
||||
goto unlock_and_fail;
|
||||
|
||||
/* Wait until the stream is ready */
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
while (1) {
|
||||
int state = pa_stream_get_state(priv->stream);
|
||||
if (state == PA_STREAM_READY)
|
||||
break;
|
||||
if (!PA_STREAM_IS_GOOD(state))
|
||||
goto unlock_and_fail;
|
||||
pa_threaded_mainloop_wait(priv->mainloop);
|
||||
}
|
||||
|
||||
if (pa_stream_get_state(priv->stream) != PA_STREAM_READY)
|
||||
if (pa_stream_is_suspended(priv->stream)) {
|
||||
MP_ERR(ao, "The stream is suspended. Bailing out.\n");
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
|
||||
@@ -391,6 +428,8 @@ unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
|
||||
fail:
|
||||
if (format)
|
||||
pa_format_info_free(format);
|
||||
if (priv->context) {
|
||||
if (!(pa_context_errno(priv->context) == PA_ERR_CONNECTIONREFUSED
|
||||
&& ao->probing))
|
||||
@@ -581,7 +620,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx,
|
||||
info_func, ao))) {
|
||||
GENERIC_ERR_MSG("pa_stream_get_sink_input_info() failed");
|
||||
GENERIC_ERR_MSG("pa_context_get_sink_input_info() failed");
|
||||
return CONTROL_ERROR;
|
||||
}
|
||||
// Warning: some information in pi might be unaccessible, because
|
||||
@@ -644,6 +683,9 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
|
||||
case AOCONTROL_UPDATE_STREAM_TITLE: {
|
||||
char *title = (char *)arg;
|
||||
pa_threaded_mainloop_lock(priv->mainloop);
|
||||
@@ -686,7 +728,7 @@ const struct ao_driver audio_out_pulse = {
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("host", cfg_host, 0),
|
||||
OPT_STRING("sink", cfg_sink, 0),
|
||||
OPT_CHOICE_OR_INT("buffer", cfg_buffer, 0, 1, 2000, ({"native", -1})),
|
||||
OPT_CHOICE_OR_INT("buffer", cfg_buffer, 0, 1, 2000, ({"native", 0})),
|
||||
OPT_FLAG("latency-hacks", cfg_latency_hacks, 0),
|
||||
{0}
|
||||
},
|
||||
|
||||
+14
-28
@@ -52,40 +52,26 @@ static int set_format(struct ao *ao)
|
||||
case AF_FORMAT_S8:
|
||||
rsd_format = RSD_S8;
|
||||
break;
|
||||
case AF_FORMAT_S16_LE:
|
||||
rsd_format = RSD_S16_LE;
|
||||
case AF_FORMAT_S16:
|
||||
rsd_format = RSD_S16_NE;
|
||||
break;
|
||||
case AF_FORMAT_S16_BE:
|
||||
rsd_format = RSD_S16_BE;
|
||||
case AF_FORMAT_U16:
|
||||
rsd_format = RSD_U16_NE;
|
||||
break;
|
||||
case AF_FORMAT_U16_LE:
|
||||
rsd_format = RSD_U16_LE;
|
||||
case AF_FORMAT_S24:
|
||||
case AF_FORMAT_U24:
|
||||
rsd_format = RSD_S32_NE;
|
||||
ao->format = AF_FORMAT_S32;
|
||||
break;
|
||||
case AF_FORMAT_U16_BE:
|
||||
rsd_format = RSD_U16_BE;
|
||||
case AF_FORMAT_S32:
|
||||
rsd_format = RSD_S32_NE;
|
||||
break;
|
||||
case AF_FORMAT_S24_LE:
|
||||
case AF_FORMAT_S24_BE:
|
||||
case AF_FORMAT_U24_LE:
|
||||
case AF_FORMAT_U24_BE:
|
||||
rsd_format = RSD_S32_LE;
|
||||
ao->format = AF_FORMAT_S32_LE;
|
||||
break;
|
||||
case AF_FORMAT_S32_LE:
|
||||
rsd_format = RSD_S32_LE;
|
||||
break;
|
||||
case AF_FORMAT_S32_BE:
|
||||
rsd_format = RSD_S32_BE;
|
||||
break;
|
||||
case AF_FORMAT_U32_LE:
|
||||
rsd_format = RSD_U32_LE;
|
||||
break;
|
||||
case AF_FORMAT_U32_BE:
|
||||
rsd_format = RSD_U32_BE;
|
||||
case AF_FORMAT_U32:
|
||||
rsd_format = RSD_U32_NE;
|
||||
break;
|
||||
default:
|
||||
rsd_format = RSD_S16_LE;
|
||||
ao->format = AF_FORMAT_S16_LE;
|
||||
rsd_format = RSD_S16_NE;
|
||||
ao->format = AF_FORMAT_S16;
|
||||
}
|
||||
|
||||
return rsd_format;
|
||||
|
||||
+32
-44
@@ -38,6 +38,20 @@ struct priv
|
||||
float buflen;
|
||||
};
|
||||
|
||||
static int fmtmap[][2] = {
|
||||
{AF_FORMAT_U8, AUDIO_U8},
|
||||
{AF_FORMAT_S8, AUDIO_S8},
|
||||
{AF_FORMAT_U16, AUDIO_U16SYS},
|
||||
{AF_FORMAT_S16, AUDIO_S16SYS},
|
||||
#ifdef AUDIO_S32SYS
|
||||
{AF_FORMAT_S32, AUDIO_S32SYS},
|
||||
#endif
|
||||
#ifdef AUDIO_F32SYS
|
||||
{AF_FORMAT_FLOAT, AUDIO_F32SYS},
|
||||
#endif
|
||||
{0}
|
||||
};
|
||||
|
||||
static void audio_callback(void *userdata, Uint8 *stream, int len)
|
||||
{
|
||||
struct ao *ao = userdata;
|
||||
@@ -104,26 +118,12 @@ static int init(struct ao *ao)
|
||||
|
||||
SDL_AudioSpec desired, obtained;
|
||||
|
||||
switch (ao->format) {
|
||||
case AF_FORMAT_U8: desired.format = AUDIO_U8; break;
|
||||
case AF_FORMAT_S8: desired.format = AUDIO_S8; break;
|
||||
case AF_FORMAT_U16_LE: desired.format = AUDIO_U16LSB; break;
|
||||
case AF_FORMAT_U16_BE: desired.format = AUDIO_U16MSB; break;
|
||||
default:
|
||||
case AF_FORMAT_S16_LE: desired.format = AUDIO_S16LSB; break;
|
||||
case AF_FORMAT_S16_BE: desired.format = AUDIO_S16MSB; break;
|
||||
#ifdef AUDIO_S32LSB
|
||||
case AF_FORMAT_S32_LE: desired.format = AUDIO_S32LSB; break;
|
||||
#endif
|
||||
#ifdef AUDIO_S32MSB
|
||||
case AF_FORMAT_S32_BE: desired.format = AUDIO_S32MSB; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32LSB
|
||||
case AF_FORMAT_FLOAT_LE: desired.format = AUDIO_F32LSB; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32MSB
|
||||
case AF_FORMAT_FLOAT_BE: desired.format = AUDIO_F32MSB; break;
|
||||
#endif
|
||||
desired.format = AUDIO_S16SYS;
|
||||
for (int n = 0; fmtmap[n][0]; n++) {
|
||||
if (ao->format == fmtmap[n][0]) {
|
||||
desired.format = fmtmap[n][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
desired.freq = ao->samplerate;
|
||||
desired.channels = ao->channels.num;
|
||||
@@ -156,30 +156,18 @@ static int init(struct ao *ao)
|
||||
// large, this will help.
|
||||
ao->device_buffer = 3 * obtained.samples;
|
||||
|
||||
switch (obtained.format) {
|
||||
case AUDIO_U8: ao->format = AF_FORMAT_U8; break;
|
||||
case AUDIO_S8: ao->format = AF_FORMAT_S8; break;
|
||||
case AUDIO_S16LSB: ao->format = AF_FORMAT_S16_LE; break;
|
||||
case AUDIO_S16MSB: ao->format = AF_FORMAT_S16_BE; break;
|
||||
case AUDIO_U16LSB: ao->format = AF_FORMAT_U16_LE; break;
|
||||
case AUDIO_U16MSB: ao->format = AF_FORMAT_U16_BE; break;
|
||||
#ifdef AUDIO_S32LSB
|
||||
case AUDIO_S32LSB: ao->format = AF_FORMAT_S32_LE; break;
|
||||
#endif
|
||||
#ifdef AUDIO_S32MSB
|
||||
case AUDIO_S32MSB: ao->format = AF_FORMAT_S32_BE; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32LSB
|
||||
case AUDIO_F32LSB: ao->format = AF_FORMAT_FLOAT_LE; break;
|
||||
#endif
|
||||
#ifdef AUDIO_F32MSB
|
||||
case AUDIO_F32MSB: ao->format = AF_FORMAT_FLOAT_BE; break;
|
||||
#endif
|
||||
default:
|
||||
if (!ao->probing)
|
||||
MP_ERR(ao, "could not find matching format\n");
|
||||
uninit(ao);
|
||||
return -1;
|
||||
ao->format = 0;
|
||||
for (int n = 0; fmtmap[n][0]; n++) {
|
||||
if (obtained.format == fmtmap[n][1]) {
|
||||
ao->format = fmtmap[n][0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ao->format) {
|
||||
if (!ao->probing)
|
||||
MP_ERR(ao, "could not find matching format\n");
|
||||
uninit(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, obtained.channels)) {
|
||||
|
||||
+59
-54
@@ -33,6 +33,7 @@ struct priv {
|
||||
struct sio_hdl *hdl;
|
||||
struct sio_par par;
|
||||
int delay;
|
||||
bool playing;
|
||||
int vol;
|
||||
int havevol;
|
||||
#define SILENCE_NMAX 0x1000
|
||||
@@ -60,6 +61,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return CONTROL_FALSE;
|
||||
sio_setvol(p->hdl, vol->left * SIO_MAXVOL / 100);
|
||||
break;
|
||||
case AOCONTROL_HAS_SOFT_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
@@ -72,7 +75,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
static void movecb(void *addr, int delta)
|
||||
{
|
||||
struct priv *p = addr;
|
||||
p->delay -= delta * (int)(p->par.bps * p->par.pchan);
|
||||
p->delay -= delta;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -107,26 +110,20 @@ static int init(struct ao *ao)
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
struct af_to_par {
|
||||
int format, bits, sig, le;
|
||||
int format, bits, sig;
|
||||
} static const af_to_par[] = {
|
||||
{AF_FORMAT_U8, 8, 0, 0},
|
||||
{AF_FORMAT_S8, 8, 1, 0},
|
||||
{AF_FORMAT_U16_LE, 16, 0, 1},
|
||||
{AF_FORMAT_U16_BE, 16, 0, 0},
|
||||
{AF_FORMAT_S16_LE, 16, 1, 1},
|
||||
{AF_FORMAT_S16_BE, 16, 1, 0},
|
||||
{AF_FORMAT_U24_LE, 16, 0, 1},
|
||||
{AF_FORMAT_U24_BE, 24, 0, 0},
|
||||
{AF_FORMAT_S24_LE, 24, 1, 1},
|
||||
{AF_FORMAT_S24_BE, 24, 1, 0},
|
||||
{AF_FORMAT_U32_LE, 32, 0, 1},
|
||||
{AF_FORMAT_U32_BE, 32, 0, 0},
|
||||
{AF_FORMAT_S32_LE, 32, 1, 1},
|
||||
{AF_FORMAT_S32_BE, 32, 1, 0}
|
||||
{AF_FORMAT_U8, 8, 0},
|
||||
{AF_FORMAT_S8, 8, 1},
|
||||
{AF_FORMAT_U16, 16, 0},
|
||||
{AF_FORMAT_S16, 16, 1},
|
||||
{AF_FORMAT_U24, 24, 0},
|
||||
{AF_FORMAT_S24, 24, 1},
|
||||
{AF_FORMAT_U32, 32, 0},
|
||||
{AF_FORMAT_S32, 32, 1},
|
||||
}, *ap;
|
||||
int i;
|
||||
|
||||
p->hdl = sio_open(p->dev, SIO_PLAY, 0);
|
||||
p->hdl = sio_open(p->dev, SIO_PLAY, 1);
|
||||
if (p->hdl == NULL) {
|
||||
MP_ERR(ao, "can't open sndio %s\n", p->dev);
|
||||
goto error;
|
||||
@@ -147,7 +144,7 @@ static int init(struct ao *ao)
|
||||
p->par.bits = ap->bits;
|
||||
p->par.sig = ap->sig;
|
||||
if (ap->bits > 8)
|
||||
p->par.le = ap->le;
|
||||
p->par.le = SIO_LE_NATIVE;
|
||||
if (ap->bits != SIO_BPS(ap->bits))
|
||||
p->par.bps = ap->bits / 8;
|
||||
break;
|
||||
@@ -173,30 +170,25 @@ static int init(struct ao *ao)
|
||||
MP_ERR(ao, "couldn't get params\n");
|
||||
goto error;
|
||||
}
|
||||
if (p->par.bps > 1 && p->par.le != SIO_LE_NATIVE) {
|
||||
MP_ERR(ao, "swapped endian output not supported\n");
|
||||
goto error;
|
||||
}
|
||||
if (p->par.bits == 8 && p->par.bps == 1) {
|
||||
ao->format = p->par.sig ? AF_FORMAT_S8 : AF_FORMAT_U8;
|
||||
} else if (p->par.bits == 16 && p->par.bps == 2) {
|
||||
ao->format = p->par.sig ?
|
||||
(p->par.le ? AF_FORMAT_S16_LE : AF_FORMAT_S16_BE) :
|
||||
(p->par.le ? AF_FORMAT_U16_LE : AF_FORMAT_U16_BE);
|
||||
ao->format = p->par.sig ? AF_FORMAT_S16 : AF_FORMAT_U16;
|
||||
} else if ((p->par.bits == 24 || p->par.msb) && p->par.bps == 3) {
|
||||
ao->format = p->par.sig ?
|
||||
(p->par.le ? AF_FORMAT_S24_LE : AF_FORMAT_S24_BE) :
|
||||
(p->par.le ? AF_FORMAT_U24_LE : AF_FORMAT_U24_BE);
|
||||
ao->format = p->par.sig ? AF_FORMAT_S24 : AF_FORMAT_U24;
|
||||
} else if ((p->par.bits == 32 || p->par.msb) && p->par.bps == 4) {
|
||||
ao->format = p->par.sig ?
|
||||
(p->par.le ? AF_FORMAT_S32_LE : AF_FORMAT_S32_BE) :
|
||||
(p->par.le ? AF_FORMAT_U32_LE : AF_FORMAT_U32_BE);
|
||||
ao->format = p->par.sig ? AF_FORMAT_S32 : AF_FORMAT_U32;
|
||||
} else {
|
||||
MP_ERR(ao, "couldn't set format\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ao->bps = p->par.bps * p->par.pchan * p->par.rate;
|
||||
ao->no_persistent_volume = true;
|
||||
p->havevol = sio_onvol(p->hdl, volcb, p);
|
||||
sio_onmove(p->hdl, movecb, p);
|
||||
p->delay = 0;
|
||||
if (!sio_start(p->hdl))
|
||||
MP_ERR(ao, "init: couldn't start\n");
|
||||
|
||||
@@ -233,46 +225,56 @@ static void reset(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
|
||||
if (!sio_stop(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't stop\n");
|
||||
p->delay = 0;
|
||||
if (!sio_start(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't start\n");
|
||||
if (p->playing) {
|
||||
MP_WARN(ao, "Blocking until remaining audio is played... (sndio design bug).\n");
|
||||
|
||||
p->playing = false;
|
||||
|
||||
if (!sio_stop(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't stop\n");
|
||||
p->delay = 0;
|
||||
if (!sio_start(p->hdl))
|
||||
MP_ERR(ao, "reset: couldn't start\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* play given number of bytes until sio_write() blocks
|
||||
* play given number of samples until sio_write() blocks
|
||||
*/
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int n;
|
||||
|
||||
n = sio_write(p->hdl, data[0], samples * ao->sstride);
|
||||
n = sio_write(p->hdl, data[0], samples * ao->sstride) / ao->sstride;
|
||||
p->delay += n;
|
||||
if (flags & AOPLAY_FINAL_CHUNK)
|
||||
reset(ao);
|
||||
return n / ao->sstride;
|
||||
p->playing = true;
|
||||
/* on AOPLAY_FINAL_CHUNK, just let it underrun */
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* how many bytes can be played without blocking
|
||||
* make libsndio call movecb()
|
||||
*/
|
||||
static void update(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int n = sio_pollfd(p->hdl, p->pfd, POLLOUT);
|
||||
while (poll(p->pfd, n, 0) < 0 && errno == EINTR) {}
|
||||
sio_revents(p->hdl, p->pfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* how many samples can be played without blocking
|
||||
*/
|
||||
static int get_space(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
int n;
|
||||
|
||||
/*
|
||||
* call poll() and sio_revents(), so the
|
||||
* delay counter is updated
|
||||
*/
|
||||
n = sio_pollfd(p->hdl, p->pfd, POLLOUT);
|
||||
while (poll(p->pfd, n, 0) < 0 && errno == EINTR)
|
||||
; /* nothing */
|
||||
sio_revents(p->hdl, p->pfd);
|
||||
update(ao);
|
||||
|
||||
return (p->par.bufsz * p->par.pchan * p->par.bps - p->delay) / ao->sstride;
|
||||
int samples = p->par.bufsz - p->delay;
|
||||
return samples / p->par.round * p->par.round;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -281,7 +283,10 @@ static int get_space(struct ao *ao)
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct priv *p = ao->priv;
|
||||
return (float)p->delay / (p->par.bps * p->par.pchan * p->par.rate);
|
||||
|
||||
update(ao);
|
||||
|
||||
return p->delay / (double)p->par.rate;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -301,8 +306,8 @@ static void audio_resume(struct ao *ao)
|
||||
int n, count, todo;
|
||||
|
||||
/*
|
||||
* we want to start with buffers full, because mplayer uses
|
||||
* get_space() pointer as clock, which would cause video to
|
||||
* we want to start with buffers full, because mpv uses
|
||||
* get_delay() as clock, which would cause video to
|
||||
* accelerate while buffers are filled.
|
||||
*/
|
||||
todo = p->par.bufsz * p->par.pchan * p->par.bps;
|
||||
|
||||
@@ -194,7 +194,6 @@ static int init(struct ao *ao)
|
||||
wasapi_enumerate_devices(state->log);
|
||||
}
|
||||
|
||||
ao->per_application_mixer = true;
|
||||
if (state->opt_exclusive) {
|
||||
state->share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
|
||||
} else {
|
||||
@@ -284,6 +283,8 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
ISimpleAudioVolume_SetMute(state->pAudioVolumeProxy, mute, NULL);
|
||||
|
||||
return CONTROL_OK;
|
||||
case AOCONTROL_HAS_PER_APP_VOLUME:
|
||||
return CONTROL_TRUE;
|
||||
case AOCONTROL_UPDATE_STREAM_TITLE: {
|
||||
MP_VERBOSE(state, "Updating stream title to \"%s\"\n", (char*)arg);
|
||||
wchar_t *title = mp_from_utf8(NULL, (char*)arg);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <mmdeviceapi.h>
|
||||
#include <avrt.h>
|
||||
|
||||
#include "compat/atomics.h"
|
||||
#include "osdep/atomics.h"
|
||||
|
||||
typedef struct wasapi_state {
|
||||
struct mp_log *log;
|
||||
|
||||
+22
-32
@@ -142,47 +142,36 @@ static void set_format(WAVEFORMATEXTENSIBLE *wformat, WORD bytepersample,
|
||||
wformat->dwChannelMask = chanmask;
|
||||
}
|
||||
|
||||
static int format_set_bits(int old_format, int bits, int fp) {
|
||||
int format = old_format;
|
||||
format &= (~AF_FORMAT_BITS_MASK) & (~AF_FORMAT_POINT_MASK) & (~AF_FORMAT_SIGN_MASK);
|
||||
format |= AF_FORMAT_SI;
|
||||
|
||||
switch (bits) {
|
||||
case 32:
|
||||
format |= AF_FORMAT_32BIT;
|
||||
break;
|
||||
case 24:
|
||||
format |= AF_FORMAT_24BIT;
|
||||
break;
|
||||
case 16:
|
||||
format |= AF_FORMAT_16BIT;
|
||||
break;
|
||||
default:
|
||||
abort(); // (should be) unreachable
|
||||
}
|
||||
|
||||
static int format_set_bits(int old_format, int bits, int fp)
|
||||
{
|
||||
if (fp) {
|
||||
format |= AF_FORMAT_F;
|
||||
} else {
|
||||
format |= AF_FORMAT_I;
|
||||
switch (bits) {
|
||||
case 64: return AF_FORMAT_DOUBLE;
|
||||
case 32: return AF_FORMAT_FLOAT;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
return af_fmt_change_bits(old_format, bits);
|
||||
}
|
||||
|
||||
static int set_ao_format(struct wasapi_state *state,
|
||||
struct ao *const ao,
|
||||
WAVEFORMATEXTENSIBLE wformat) {
|
||||
// .Data1 == 1 is PCM, .Data1 == 3 is IEEE_FLOAT
|
||||
int format = format_set_bits(ao->format,
|
||||
wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3);
|
||||
|
||||
WAVEFORMATEXTENSIBLE wformat)
|
||||
{
|
||||
if (wformat.SubFormat.Data1 != 1 && wformat.SubFormat.Data1 != 3) {
|
||||
MP_ERR(ao, "unknown SubFormat %"PRIu32"\n",
|
||||
(uint32_t)wformat.SubFormat.Data1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// .Data1 == 1 is PCM, .Data1 == 3 is IEEE_FLOAT
|
||||
int format = format_set_bits(ao->format,
|
||||
wformat.Format.wBitsPerSample, wformat.SubFormat.Data1 == 3);
|
||||
|
||||
if (!format)
|
||||
return 0;
|
||||
|
||||
ao->samplerate = wformat.Format.nSamplesPerSec;
|
||||
ao->bps = wformat.Format.nAvgBytesPerSec;
|
||||
ao->format = format;
|
||||
@@ -204,6 +193,8 @@ static int try_format(struct wasapi_state *state,
|
||||
set_format(&wformat, bits / 8, samplerate, channels.num, mp_chmap_to_waveext(&channels));
|
||||
|
||||
int af_format = format_set_bits(ao->format, bits, bits == 32);
|
||||
if (!af_format)
|
||||
return 0;
|
||||
|
||||
MP_VERBOSE(ao, "trying %dch %s @ %dhz\n",
|
||||
channels.num, af_fmt_to_str(af_format), samplerate);
|
||||
@@ -292,14 +283,13 @@ static int try_passthrough(struct wasapi_state *state,
|
||||
union WAVEFMT u;
|
||||
u.extensible = &wformat;
|
||||
|
||||
MP_VERBOSE(ao, "trying passthrough for %s...\n",
|
||||
af_fmt_to_str((ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE));
|
||||
MP_VERBOSE(ao, "trying passthrough for %s...\n", af_fmt_to_str(ao->format));
|
||||
|
||||
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
|
||||
state->share_mode,
|
||||
u.ex, NULL);
|
||||
if (!FAILED(hr)) {
|
||||
ao->format = (ao->format&~AF_FORMAT_END_MASK) | AF_FORMAT_LE;
|
||||
ao->format = ao->format;
|
||||
state->format = wformat;
|
||||
return 1;
|
||||
}
|
||||
@@ -310,7 +300,7 @@ static int find_formats(struct ao *const ao)
|
||||
{
|
||||
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
|
||||
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format) || ao->format == AF_FORMAT_MPEG2) {
|
||||
if (AF_FORMAT_IS_IEC61937(ao->format)) {
|
||||
if (try_passthrough(state, ao))
|
||||
return 0;
|
||||
|
||||
|
||||
+3
-10
@@ -25,14 +25,6 @@
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/chmap_sel.h"
|
||||
|
||||
// Minimum buffer size in seconds.
|
||||
#define MIN_BUFFER 0.2
|
||||
|
||||
// If ao_get_delay() reaches this value after ao_play() was called with the
|
||||
// AOPLAY_FINAL_CHUNK flag set, the playback core expects that the audio has
|
||||
// all been played.
|
||||
#define AO_EOF_DELAY 0.05
|
||||
|
||||
/* global data used by ao.c and ao drivers */
|
||||
struct ao {
|
||||
int samplerate;
|
||||
@@ -44,8 +36,6 @@ struct ao {
|
||||
int num_planes;
|
||||
bool probing; // if true, don't fail loudly on init
|
||||
bool untimed; // don't assume realtime playback
|
||||
bool no_persistent_volume; // the AO does the equivalent of af_volume
|
||||
bool per_application_mixer; // like above, but volume persists (per app)
|
||||
int device_buffer; // device buffer in samples (guessed by
|
||||
// common init code if not set by driver)
|
||||
const struct ao_driver *api; // entrypoints to the wrapper (push.c/pull.c)
|
||||
@@ -56,6 +46,7 @@ struct ao {
|
||||
struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix
|
||||
|
||||
int buffer;
|
||||
double def_buffer;
|
||||
void *api_priv;
|
||||
};
|
||||
|
||||
@@ -134,6 +125,8 @@ struct ao_driver {
|
||||
float (*get_delay)(struct ao *ao);
|
||||
// push based: block until all queued audio is played (optional)
|
||||
void (*drain)(struct ao *ao);
|
||||
// Optional. Return true if audio has stopped in any way.
|
||||
bool (*get_eof)(struct ao *ao);
|
||||
// Wait until the audio buffer needs to be refilled. The lock is the
|
||||
// internal mutex usually protecting the internal AO state (and used to
|
||||
// protect driver calls), and must be temporarily unlocked while waiting.
|
||||
|
||||
+10
-1
@@ -30,7 +30,7 @@
|
||||
|
||||
#include "osdep/timer.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "compat/atomics.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "misc/ring.h"
|
||||
|
||||
/*
|
||||
@@ -201,6 +201,14 @@ static void drain(struct ao *ao)
|
||||
reset(ao);
|
||||
}
|
||||
|
||||
static bool get_eof(struct ao *ao)
|
||||
{
|
||||
struct ao_pull_state *p = ao->api_priv;
|
||||
// For simplicity, ignore the latency. Otherwise, we would have to run an
|
||||
// extra thread to time it.
|
||||
return mp_ring_buffered(p->buffers[0]) == 0;
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
ao->driver->uninit(ao);
|
||||
@@ -225,6 +233,7 @@ const struct ao_driver ao_api_pull = {
|
||||
.get_space = get_space,
|
||||
.play = play,
|
||||
.get_delay = get_delay,
|
||||
.get_eof = get_eof,
|
||||
.pause = pause,
|
||||
.resume = resume,
|
||||
.priv_size = sizeof(struct ao_pull_state),
|
||||
|
||||
+126
-92
@@ -35,7 +35,7 @@
|
||||
|
||||
#include "osdep/threads.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "compat/atomics.h"
|
||||
#include "osdep/atomics.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
@@ -44,18 +44,15 @@ struct ao_push_state {
|
||||
pthread_t thread;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t wakeup;
|
||||
pthread_cond_t wakeup_drain;
|
||||
|
||||
// --- protected by lock
|
||||
|
||||
struct mp_audio_buffer *buffer;
|
||||
|
||||
bool terminate;
|
||||
bool drain;
|
||||
bool buffers_full;
|
||||
bool avoid_ao_wait;
|
||||
bool wait_on_ao;
|
||||
bool still_playing;
|
||||
bool need_wakeup;
|
||||
bool requested_data;
|
||||
bool paused;
|
||||
|
||||
// Whether the current buffer contains the complete audio.
|
||||
@@ -87,22 +84,21 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
|
||||
return r;
|
||||
}
|
||||
|
||||
static double unlocked_get_delay(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
double driver_delay = 0;
|
||||
if (ao->driver->get_delay)
|
||||
driver_delay = ao->driver->get_delay(ao);
|
||||
return driver_delay + mp_audio_buffer_seconds(p->buffer);
|
||||
}
|
||||
|
||||
static float get_delay(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
pthread_mutex_lock(&p->lock);
|
||||
double driver_delay = 0;
|
||||
if (ao->driver->get_delay)
|
||||
driver_delay = ao->driver->get_delay(ao);
|
||||
double delay = driver_delay + mp_audio_buffer_seconds(p->buffer);
|
||||
float delay = unlocked_get_delay(ao);
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
if (delay >= AO_EOF_DELAY && p->expected_end_time) {
|
||||
if (mp_time_sec() > p->expected_end_time) {
|
||||
MP_ERR(ao, "Audio device EOF reporting is broken!\n");
|
||||
MP_ERR(ao, "Please report this problem.\n");
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
@@ -114,7 +110,9 @@ static void reset(struct ao *ao)
|
||||
ao->driver->reset(ao);
|
||||
mp_audio_buffer_clear(p->buffer);
|
||||
p->paused = false;
|
||||
wakeup_playthread(ao);
|
||||
if (p->still_playing)
|
||||
wakeup_playthread(ao);
|
||||
p->still_playing = false;
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
}
|
||||
|
||||
@@ -145,20 +143,27 @@ static void drain(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
|
||||
MP_VERBOSE(ao, "draining...\n");
|
||||
|
||||
pthread_mutex_lock(&p->lock);
|
||||
if (p->paused) {
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
return;
|
||||
}
|
||||
if (p->paused)
|
||||
goto done;
|
||||
|
||||
p->final_chunk = true;
|
||||
p->drain = true;
|
||||
wakeup_playthread(ao);
|
||||
while (p->drain)
|
||||
pthread_cond_wait(&p->wakeup_drain, &p->lock);
|
||||
while (p->still_playing && mp_audio_buffer_samples(p->buffer) > 0)
|
||||
pthread_cond_wait(&p->wakeup, &p->lock);
|
||||
|
||||
if (ao->driver->drain) {
|
||||
ao->driver->drain(ao);
|
||||
} else {
|
||||
double time = unlocked_get_delay(ao);
|
||||
mp_sleep_us(MPMIN(time, ao->buffer / (double)ao->samplerate + 1) * 1e6);
|
||||
}
|
||||
|
||||
done:
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
|
||||
if (!ao->driver->drain)
|
||||
mp_sleep_us(get_delay(ao) * 1000000);
|
||||
reset(ao);
|
||||
}
|
||||
|
||||
@@ -168,13 +173,17 @@ static int unlocked_get_space(struct ao *ao)
|
||||
int space = mp_audio_buffer_get_write_available(p->buffer);
|
||||
if (ao->driver->get_space) {
|
||||
// The following code attempts to keep the total buffered audio to
|
||||
// MIN_BUFFER/2+device_buffer in order to improve latency.
|
||||
// ao->buffer in order to improve latency.
|
||||
int device_space = ao->driver->get_space(ao);
|
||||
int device_buffered = ao->device_buffer - device_space;
|
||||
int soft_buffered = mp_audio_buffer_samples(p->buffer);
|
||||
int min_buffer = MIN_BUFFER / 2 * ao->samplerate + ao->device_buffer;
|
||||
int total_buffer = device_buffered + soft_buffered;
|
||||
int missing = min_buffer - total_buffer;
|
||||
// The extra margin helps avoiding too many wakeups if the AO is fully
|
||||
// byte based and doesn't do proper chunked processing.
|
||||
int min_buffer = ao->buffer + 64;
|
||||
int missing = min_buffer - device_buffered - soft_buffered;
|
||||
// But always keep the device's buffer filled as much as we can.
|
||||
int device_missing = device_space - soft_buffered;
|
||||
missing = MPMAX(missing, device_missing);
|
||||
space = MPMIN(space, missing);
|
||||
space = MPMAX(0, space);
|
||||
}
|
||||
@@ -190,6 +199,15 @@ static int get_space(struct ao *ao)
|
||||
return space;
|
||||
}
|
||||
|
||||
static bool get_eof(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
pthread_mutex_lock(&p->lock);
|
||||
bool eof = !p->still_playing;
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
return eof;
|
||||
}
|
||||
|
||||
static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
@@ -199,6 +217,8 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
int write_samples = mp_audio_buffer_get_write_available(p->buffer);
|
||||
write_samples = MPMIN(write_samples, samples);
|
||||
|
||||
MP_TRACE(ao, "samples=%d flags=%d r=%d\n", samples, flags, write_samples);
|
||||
|
||||
if (write_samples < samples)
|
||||
flags = flags & ~AOPLAY_FINAL_CHUNK;
|
||||
bool is_final = flags & AOPLAY_FINAL_CHUNK;
|
||||
@@ -212,16 +232,14 @@ static int play(struct ao *ao, void **data, int samples, int flags)
|
||||
|
||||
bool got_data = write_samples > 0 || p->paused || p->final_chunk != is_final;
|
||||
|
||||
p->expected_end_time = 0;
|
||||
p->final_chunk = is_final;
|
||||
p->paused = false;
|
||||
p->still_playing |= write_samples > 0;
|
||||
|
||||
// If we don't have new data, the decoder thread basically promises it
|
||||
// will send new data as soon as it's available.
|
||||
if (got_data) {
|
||||
p->requested_data = false;
|
||||
if (got_data)
|
||||
wakeup_playthread(ao);
|
||||
}
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
return write_samples;
|
||||
}
|
||||
@@ -251,37 +269,31 @@ static void ao_play_data(struct ao *ao)
|
||||
}
|
||||
r = MPMAX(r, 0);
|
||||
// Probably can't copy the rest of the buffer due to period alignment.
|
||||
bool stuck = r <= 0 && space >= max && data.samples > 0;
|
||||
if ((flags & AOPLAY_FINAL_CHUNK) && stuck) {
|
||||
bool stuck_eof = r <= 0 && space >= max && data.samples > 0;
|
||||
if ((flags & AOPLAY_FINAL_CHUNK) && stuck_eof) {
|
||||
MP_ERR(ao, "Audio output driver seems to ignore AOPLAY_FINAL_CHUNK.\n");
|
||||
r = max;
|
||||
}
|
||||
mp_audio_buffer_skip(p->buffer, r);
|
||||
if (p->final_chunk && mp_audio_buffer_samples(p->buffer) == 0) {
|
||||
p->expected_end_time = mp_time_sec() + AO_EOF_DELAY + 0.25; // + margin
|
||||
if (ao->driver->get_delay)
|
||||
p->expected_end_time += ao->driver->get_delay(ao);
|
||||
}
|
||||
// In both cases, we have to account for space!=0, but the AO not accepting
|
||||
// any new data (due to rounding to period boundaries).
|
||||
p->buffers_full = max >= space && r <= 0;
|
||||
p->avoid_ao_wait = (max == 0 && space > 0) || p->paused || stuck;
|
||||
MP_TRACE(ao, "in=%d, space=%d r=%d flags=%d aw=%d full=%d f=%d\n", max,
|
||||
space, r, flags, p->avoid_ao_wait, p->buffers_full, p->final_chunk);
|
||||
}
|
||||
|
||||
// Estimate when the AO needs data again.
|
||||
static double ao_estimate_timeout(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
double timeout = 0;
|
||||
if (p->buffers_full && ao->driver->get_delay) {
|
||||
timeout = ao->driver->get_delay(ao) - 0.050;
|
||||
// Keep extra safety margin if the buffers are large
|
||||
if (timeout > 0.100)
|
||||
timeout = MPMAX(timeout - 0.200, 0.100);
|
||||
}
|
||||
return MPMAX(timeout, ao->device_buffer * 0.75 / ao->samplerate);
|
||||
if (r > 0)
|
||||
p->expected_end_time = 0;
|
||||
// Nothing written, but more input data than space - this must mean the
|
||||
// AO's get_space() doesn't do period alignment correctly.
|
||||
bool stuck = r == 0 && max >= space && space > 0;
|
||||
if (stuck)
|
||||
MP_ERR(ao, "Audio output is reporting incorrect buffer status.\n");
|
||||
// Wait until space becomes available. Also wait if we actually wrote data,
|
||||
// so the AO wakes us up properly if it needs more data.
|
||||
p->wait_on_ao = space == 0 || r > 0 || stuck;
|
||||
p->still_playing |= r > 0;
|
||||
// If we just filled the AO completely (r == space), don't refill for a
|
||||
// while. Prevents wakeup feedback with byte-granular AOs.
|
||||
int needed = unlocked_get_space(ao);
|
||||
bool more = needed >= (r == space ? ao->device_buffer / 4 : 1) && !stuck;
|
||||
if (more)
|
||||
mp_input_wakeup(ao->input_ctx); // request more data
|
||||
MP_TRACE(ao, "in=%d flags=%d space=%d r=%d wa=%d needed=%d more=%d\n",
|
||||
max, flags, space, r, p->wait_on_ao, needed, more);
|
||||
}
|
||||
|
||||
static void *playthread(void *arg)
|
||||
@@ -293,25 +305,9 @@ static void *playthread(void *arg)
|
||||
if (!p->paused)
|
||||
ao_play_data(ao);
|
||||
|
||||
// Request new data from decoder if buffer goes below "full".
|
||||
// Allow a small margin of missing data for AOs that use timeouts.
|
||||
double margin = ao->driver->wait ? 0 : ao->device_buffer / 8;
|
||||
if (!p->buffers_full && unlocked_get_space(ao) > margin) {
|
||||
if (!p->requested_data)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
p->requested_data = true;
|
||||
}
|
||||
|
||||
if (p->drain && (p->avoid_ao_wait || p->paused)) {
|
||||
if (ao->driver->drain)
|
||||
ao->driver->drain(ao);
|
||||
p->drain = false;
|
||||
pthread_cond_signal(&p->wakeup_drain);
|
||||
}
|
||||
|
||||
if (!p->need_wakeup) {
|
||||
MP_STATS(ao, "start audio wait");
|
||||
if (p->avoid_ao_wait || p->paused) {
|
||||
if (!p->wait_on_ao || p->paused) {
|
||||
// Avoid busy waiting, because the audio API will still report
|
||||
// that it needs new data, even if we're not ready yet, or if
|
||||
// get_space() decides that the amount of audio buffered in the
|
||||
@@ -319,12 +315,39 @@ static void *playthread(void *arg)
|
||||
// The most important part is that the decoder is woken up, so
|
||||
// that the decoder will wake up us in turn.
|
||||
MP_TRACE(ao, "buffer inactive.\n");
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
pthread_cond_wait(&p->wakeup, &p->lock);
|
||||
|
||||
bool was_playing = p->still_playing;
|
||||
double timeout = -1;
|
||||
if (p->still_playing && !p->paused && p->final_chunk &&
|
||||
!mp_audio_buffer_samples(p->buffer))
|
||||
{
|
||||
double now = mp_time_sec();
|
||||
if (!p->expected_end_time)
|
||||
p->expected_end_time = now + unlocked_get_delay(ao);
|
||||
if (p->expected_end_time < now) {
|
||||
p->still_playing = false;
|
||||
} else {
|
||||
timeout = p->expected_end_time - now;
|
||||
}
|
||||
}
|
||||
|
||||
if (was_playing && !p->still_playing)
|
||||
mp_input_wakeup(ao->input_ctx);
|
||||
pthread_cond_signal(&p->wakeup); // for draining
|
||||
|
||||
if (p->still_playing && timeout > 0) {
|
||||
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
|
||||
} else {
|
||||
pthread_cond_wait(&p->wakeup, &p->lock);
|
||||
}
|
||||
} else {
|
||||
// Wait until the device wants us to write more data to it.
|
||||
if (!ao->driver->wait || ao->driver->wait(ao, &p->lock) < 0) {
|
||||
// Fallback to guessing.
|
||||
double timeout = ao_estimate_timeout(ao);
|
||||
double timeout = 0;
|
||||
if (ao->driver->get_delay)
|
||||
timeout = ao->driver->get_delay(ao);
|
||||
timeout *= 0.25; // wake up if 25% played
|
||||
mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout);
|
||||
}
|
||||
}
|
||||
@@ -336,6 +359,19 @@ static void *playthread(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void destroy_no_thread(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
|
||||
ao->driver->uninit(ao);
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
close(p->wakeup_pipe[n]);
|
||||
|
||||
pthread_cond_destroy(&p->wakeup);
|
||||
pthread_mutex_destroy(&p->lock);
|
||||
}
|
||||
|
||||
static void uninit(struct ao *ao)
|
||||
{
|
||||
struct ao_push_state *p = ao->api_priv;
|
||||
@@ -347,14 +383,7 @@ static void uninit(struct ao *ao)
|
||||
|
||||
pthread_join(p->thread, NULL);
|
||||
|
||||
ao->driver->uninit(ao);
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
close(p->wakeup_pipe[n]);
|
||||
|
||||
pthread_cond_destroy(&p->wakeup);
|
||||
pthread_cond_destroy(&p->wakeup_drain);
|
||||
pthread_mutex_destroy(&p->lock);
|
||||
destroy_no_thread(ao);
|
||||
}
|
||||
|
||||
static int init(struct ao *ao)
|
||||
@@ -363,9 +392,13 @@ static int init(struct ao *ao)
|
||||
|
||||
pthread_mutex_init(&p->lock, NULL);
|
||||
pthread_cond_init(&p->wakeup, NULL);
|
||||
pthread_cond_init(&p->wakeup_drain, NULL);
|
||||
mp_make_wakeup_pipe(p->wakeup_pipe);
|
||||
|
||||
if (ao->device_buffer <= 0) {
|
||||
MP_FATAL(ao, "Couldn't probe device buffer size.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->buffer = mp_audio_buffer_create(ao);
|
||||
mp_audio_buffer_reinit_fmt(p->buffer, ao->format,
|
||||
&ao->channels, ao->samplerate);
|
||||
@@ -374,7 +407,7 @@ static int init(struct ao *ao)
|
||||
goto err;
|
||||
return 0;
|
||||
err:
|
||||
ao->driver->uninit(ao);
|
||||
destroy_no_thread(ao);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -389,6 +422,7 @@ const struct ao_driver ao_api_push = {
|
||||
.pause = audio_pause,
|
||||
.resume = resume,
|
||||
.drain = drain,
|
||||
.get_eof = get_eof,
|
||||
.priv_size = sizeof(struct ao_push_state),
|
||||
};
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt, AVRational *tb)
|
||||
if (mpkt && mpkt->avpacket) {
|
||||
dst->side_data = mpkt->avpacket->side_data;
|
||||
dst->side_data_elems = mpkt->avpacket->side_data_elems;
|
||||
if (dst->data == mpkt->avpacket->data)
|
||||
dst->buf = mpkt->avpacket->buf;
|
||||
}
|
||||
if (mpkt && tb && tb->num > 0 && tb->den > 0)
|
||||
dst->duration = mpkt->duration / av_q2d(*tb);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include "talloc.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "common/msg.h"
|
||||
#include "codecs.h"
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#define appendf(ptr, ...) \
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "compat/compiler.h"
|
||||
#include "osdep/compiler.h"
|
||||
#include "talloc.h"
|
||||
|
||||
// double should be able to represent this exactly
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
struct mpv_global {
|
||||
struct MPOpts *opts;
|
||||
struct mp_log *log;
|
||||
|
||||
int (*stream_interrupt_cb)(void *ctx);
|
||||
void *stream_interrupt_cb_ctx;
|
||||
};
|
||||
|
||||
#endif
|
||||
+40
-31
@@ -27,8 +27,8 @@
|
||||
|
||||
#include "talloc.h"
|
||||
|
||||
#include "bstr/bstr.h"
|
||||
#include "compat/atomics.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "osdep/atomics.h"
|
||||
#include "common/common.h"
|
||||
#include "common/global.h"
|
||||
#include "misc/ring.h"
|
||||
@@ -158,16 +158,10 @@ static void prepare_status_line(struct mp_log_root *root, char *new_status)
|
||||
size_t clear_lines = MPMIN(MPMAX(new_lines, old_lines), root->blank_lines);
|
||||
|
||||
// clear the status line itself
|
||||
if (terminal_erase_to_end_of_line[0]) {
|
||||
fprintf(f, "\r%s", terminal_erase_to_end_of_line);
|
||||
} else {
|
||||
// This code is for MS windows (no ANSI control sequences)
|
||||
get_screen_size();
|
||||
fprintf(f, "\r%*s\r", screen_width - 1, "");
|
||||
}
|
||||
fprintf(f, "\r\033[K");
|
||||
// and clear all previous old lines
|
||||
for (size_t n = 1; n < clear_lines; n++)
|
||||
fprintf(f, "%s\r%s", terminal_cursor_up, terminal_erase_to_end_of_line);
|
||||
fprintf(f, "\033[A\r\033[K");
|
||||
// skip "unused" blank lines, so that status is aligned to term bottom
|
||||
for (size_t n = new_lines; n < clear_lines; n++)
|
||||
fprintf(f, "\n");
|
||||
@@ -200,10 +194,20 @@ bool mp_msg_has_status_line(struct mpv_global *global)
|
||||
return r;
|
||||
}
|
||||
|
||||
static void set_term_color(FILE *stream, int c)
|
||||
{
|
||||
if (c == -1) {
|
||||
fprintf(stream, "\033[0m");
|
||||
} else {
|
||||
fprintf(stream, "\033[%d;3%dm", c >> 3, c & 7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void set_msg_color(FILE* stream, int lev)
|
||||
{
|
||||
static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, 8, -1};
|
||||
terminal_set_foreground_color(stream, v_colors[lev]);
|
||||
set_term_color(stream, v_colors[lev]);
|
||||
}
|
||||
|
||||
static void pretty_print_module(FILE* stream, const char *prefix, bool use_color, int lev)
|
||||
@@ -214,12 +218,12 @@ static void pretty_print_module(FILE* stream, const char *prefix, bool use_color
|
||||
unsigned int mod = 0;
|
||||
for (int i = 0; i < prefix_len; ++i)
|
||||
mod = mod * 33 + prefix[i];
|
||||
terminal_set_foreground_color(stream, (mod + 1) % 15 + 1);
|
||||
set_term_color(stream, (mod + 1) % 15 + 1);
|
||||
}
|
||||
|
||||
fprintf(stream, "%10s", prefix);
|
||||
if (use_color)
|
||||
terminal_set_foreground_color(stream, -1);
|
||||
set_term_color(stream, -1);
|
||||
fprintf(stream, ": ");
|
||||
if (use_color)
|
||||
set_msg_color(stream, lev);
|
||||
@@ -291,7 +295,7 @@ static void print_msg_on_terminal(struct mp_log *log, int lev, char *text)
|
||||
fprintf(stream, "%s", terminate);
|
||||
|
||||
if (root->color)
|
||||
terminal_set_foreground_color(stream, -1);
|
||||
set_term_color(stream, -1);
|
||||
fflush(stream);
|
||||
}
|
||||
|
||||
@@ -363,34 +367,39 @@ void mp_msg_va(struct mp_log *log, int lev, const char *format, va_list va)
|
||||
// parent's name. If the name starts with "/", the parent's name is not
|
||||
// prefixed (except in verbose mode), and if it starts with "!", the name is
|
||||
// not printed at all (except in verbose mode).
|
||||
// If name is NULL, the parent's name/prefix is used.
|
||||
// Thread-safety: fully thread-safe, but keep in mind that talloc is not (so
|
||||
// talloc_ctx must be owned by the current thread).
|
||||
struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent,
|
||||
const char *name)
|
||||
{
|
||||
assert(parent);
|
||||
assert(name);
|
||||
struct mp_log *log = talloc_zero(talloc_ctx, struct mp_log);
|
||||
if (!parent->root)
|
||||
return log; // same as null_log
|
||||
log->root = parent->root;
|
||||
if (name[0] == '!') {
|
||||
name = &name[1];
|
||||
} else if (name[0] == '/') {
|
||||
name = &name[1];
|
||||
log->prefix = talloc_strdup(log, name);
|
||||
} else {
|
||||
log->prefix = parent->prefix
|
||||
if (name) {
|
||||
if (name[0] == '!') {
|
||||
name = &name[1];
|
||||
} else if (name[0] == '/') {
|
||||
name = &name[1];
|
||||
log->prefix = talloc_strdup(log, name);
|
||||
} else {
|
||||
log->prefix = parent->prefix
|
||||
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
||||
: talloc_strdup(log, name);
|
||||
}
|
||||
log->verbose_prefix = parent->prefix
|
||||
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
||||
: talloc_strdup(log, name);
|
||||
if (log->prefix && !log->prefix[0])
|
||||
log->prefix = NULL;
|
||||
if (!log->verbose_prefix[0])
|
||||
log->verbose_prefix = "global";
|
||||
} else {
|
||||
log->prefix = talloc_strdup(log, parent->prefix);
|
||||
log->verbose_prefix = talloc_strdup(log, parent->verbose_prefix);
|
||||
}
|
||||
log->verbose_prefix = parent->prefix
|
||||
? talloc_asprintf(log, "%s/%s", parent->prefix, name)
|
||||
: talloc_strdup(log, name);
|
||||
if (log->prefix && !log->prefix[0])
|
||||
log->prefix = NULL;
|
||||
if (!log->verbose_prefix[0])
|
||||
log->verbose_prefix = "global";
|
||||
return log;
|
||||
}
|
||||
|
||||
@@ -428,8 +437,8 @@ void mp_msg_update_msglevels(struct mpv_global *global)
|
||||
root->use_terminal = opts->use_terminal;
|
||||
root->show_time = opts->msg_time;
|
||||
if (root->use_terminal) {
|
||||
root->color = opts->msg_color && isatty(fileno(stdout));
|
||||
root->termosd = isatty(fileno(stderr));
|
||||
root->color = opts->msg_color && isatty(STDOUT_FILENO);
|
||||
root->termosd = isatty(STDERR_FILENO);
|
||||
}
|
||||
|
||||
talloc_free(root->msglevels);
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "compat/compiler.h"
|
||||
#include "osdep/compiler.h"
|
||||
|
||||
struct mp_log;
|
||||
|
||||
|
||||
+11
-5
@@ -30,7 +30,8 @@
|
||||
struct playlist_entry *playlist_entry_new(const char *filename)
|
||||
{
|
||||
struct playlist_entry *e = talloc_zero(NULL, struct playlist_entry);
|
||||
e->filename = talloc_strdup(e, filename);
|
||||
char *local_filename = mp_file_url_to_filename(e, bstr0(filename));
|
||||
e->filename = local_filename ? local_filename : talloc_strdup(e, filename);
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -105,10 +106,18 @@ static void playlist_unlink(struct playlist *pl, struct playlist_entry *entry)
|
||||
entry->pl = NULL;
|
||||
}
|
||||
|
||||
void playlist_entry_unref(struct playlist_entry *e)
|
||||
{
|
||||
e->reserved--;
|
||||
if (e->reserved < 0)
|
||||
talloc_free(e);
|
||||
}
|
||||
|
||||
void playlist_remove(struct playlist *pl, struct playlist_entry *entry)
|
||||
{
|
||||
playlist_unlink(pl, entry);
|
||||
talloc_free(entry);
|
||||
entry->removed = true;
|
||||
playlist_entry_unref(entry);
|
||||
}
|
||||
|
||||
void playlist_clear(struct playlist *pl)
|
||||
@@ -286,9 +295,6 @@ struct playlist *playlist_parse_file(const char *file, struct mpv_global *global
|
||||
if (ret && !ret->first)
|
||||
mp_warn(log, "Warning: empty playlist\n");
|
||||
|
||||
if (ret)
|
||||
playlist_add_base_path(ret, mp_dirname(file));
|
||||
|
||||
talloc_free(log);
|
||||
return ret;
|
||||
}
|
||||
+14
-1
@@ -19,7 +19,7 @@
|
||||
#define MPLAYER_PLAYLIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
struct playlist_param {
|
||||
bstr name, value;
|
||||
@@ -41,6 +41,17 @@ struct playlist_entry {
|
||||
bool playback_short : 1;
|
||||
// Set to true if not at least 1 frame (audio or video) could be played.
|
||||
bool init_failed : 1;
|
||||
// Entry was removed with playlist_remove (etc.), but not deallocated.
|
||||
bool removed : 1;
|
||||
// Additional refcount. Normally (reserved==0), the entry is owned by the
|
||||
// playlist, and this can be used to keep the entry alive.
|
||||
int reserved;
|
||||
// Used to reject loading of unsafe entries from external playlists.
|
||||
// Can have any of the following bit flags set:
|
||||
// STREAM_SAFE_ONLY: only allow streams marked with is_safe
|
||||
// STREAM_NETWORK_ONLY: only allow streams marked with is_network
|
||||
// The value 0 allows everything.
|
||||
int stream_flags;
|
||||
};
|
||||
|
||||
struct playlist {
|
||||
@@ -83,4 +94,6 @@ struct playlist_entry *playlist_entry_from_index(struct playlist *pl, int index)
|
||||
struct mpv_global;
|
||||
struct playlist *playlist_parse_file(const char *file, struct mpv_global *global);
|
||||
|
||||
void playlist_entry_unref(struct playlist_entry *e);
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <libavutil/dict.h>
|
||||
#include "tags.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
void mp_tags_set_str(struct mp_tags *tags, const char *key, const char *value)
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef MP_TAGS_H
|
||||
#define MP_TAGS_H
|
||||
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
struct mp_tags {
|
||||
char **keys;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_MPBSWAP_H
|
||||
#define MPLAYER_MPBSWAP_H
|
||||
|
||||
#include <libavutil/bswap.h>
|
||||
|
||||
#define bswap_16(x) av_bswap16(x)
|
||||
#define bswap_32(x) av_bswap32(x)
|
||||
#define be2me_16(x) av_be2ne16(x)
|
||||
#define be2me_32(x) av_be2ne32(x)
|
||||
#define le2me_16(x) av_le2ne16(x)
|
||||
#define le2me_32(x) av_le2ne32(x)
|
||||
#define le2me_64(x) av_le2ne64(x)
|
||||
|
||||
#endif /* MPLAYER_MPBSWAP_H */
|
||||
+44
-6
@@ -97,12 +97,6 @@ static const struct mp_codec_tag mp_audio_codec_tags[] = {
|
||||
{MKTAG('s', 'a', 'm', 'r'), "amr_nb"},
|
||||
{MKTAG('s', 'a', 'w', 'b'), "amr_wb"},
|
||||
{MKTAG('v', 'r', 'b', 's'), "vorbis"},
|
||||
// Special cased in ad_lavc:
|
||||
{0 , "pcm"},
|
||||
{0x1 , "pcm"}, // lavf: pcm_s16le
|
||||
{0x3 , "pcm"}, // lavf: pcm_f32le
|
||||
{0xfffe , "pcm"},
|
||||
{MKTAG('t', 'w', 'o', 's'), "pcm"},
|
||||
// ------- internal mplayer FourCCs ------
|
||||
{MKTAG('O', 'p', 'u', 's'), "opus"}, // demux_mkv.c
|
||||
{MKTAG('a', 'L', 'a', 'C'), "alac"}, // demux_mkv.c
|
||||
@@ -358,6 +352,28 @@ static const char *lookup_tag(const struct mp_codec_tag *mp_table,
|
||||
return id == AV_CODEC_ID_NONE ? NULL : mp_codec_from_av_codec_id(id);
|
||||
}
|
||||
|
||||
static const char *pcm_le[] = {"pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"};
|
||||
static const char *pcm_be[] = {"pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"};
|
||||
|
||||
static const char *map_audio_pcm_tag(uint32_t tag, int bits)
|
||||
{
|
||||
int bytes = (bits + 7) / 8;
|
||||
switch (tag) {
|
||||
case 0x0: // Microsoft PCM
|
||||
case 0x1:
|
||||
case 0xfffe: // MS PCM, Extended
|
||||
return bytes >= 1 && bytes <= 4 ? pcm_le[bytes - 1] : NULL;
|
||||
case 0x3: // IEEE float
|
||||
return bits == 64 ? "pcm_f64le" : "pcm_f32le";
|
||||
case 0x20776172:// 'raw '
|
||||
return bits == 8 ? "pcm_u8" : "pcm_s16be";
|
||||
case MKTAG('t', 'w', 'o', 's'): // demux_mkv.c internal
|
||||
return bytes >= 1 && bytes <= 4 ? pcm_be[bytes - 1] : NULL;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void mp_set_codec_from_tag(struct sh_stream *sh)
|
||||
{
|
||||
switch (sh->type) {
|
||||
@@ -370,6 +386,12 @@ void mp_set_codec_from_tag(struct sh_stream *sh)
|
||||
sh->codec = lookup_tag(mp_audio_codec_tags,
|
||||
avformat_get_riff_audio_tags(),
|
||||
sh->format);
|
||||
if (sh->audio && sh->audio->bits_per_coded_sample) {
|
||||
const char *codec =
|
||||
map_audio_pcm_tag(sh->format, sh->audio->bits_per_coded_sample);
|
||||
if (codec)
|
||||
sh->codec = codec;
|
||||
}
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
@@ -383,3 +405,19 @@ uint32_t mp_video_fourcc_alias(uint32_t fourcc)
|
||||
}
|
||||
return fourcc;
|
||||
}
|
||||
|
||||
void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
|
||||
bool is_be)
|
||||
{
|
||||
// This uses libavcodec pcm codec names, e.g. "pcm_u16le".
|
||||
char codec[64] = "pcm_";
|
||||
if (is_float) {
|
||||
mp_snprintf_cat(codec, sizeof(codec), "f");
|
||||
} else {
|
||||
mp_snprintf_cat(codec, sizeof(codec), sign ? "s" : "u");
|
||||
}
|
||||
mp_snprintf_cat(codec, sizeof(codec), "%d", bits);
|
||||
if (bits != 8)
|
||||
mp_snprintf_cat(codec, sizeof(codec), is_be ? "be" : "le");
|
||||
sh->codec = talloc_strdup(sh->audio, codec);
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
#define MP_CODEC_TAGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
uint32_t mp_video_fourcc_alias(uint32_t fourcc);
|
||||
|
||||
@@ -26,4 +27,7 @@ struct sh_stream;
|
||||
|
||||
void mp_set_codec_from_tag(struct sh_stream *sh);
|
||||
|
||||
void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
|
||||
bool is_be);
|
||||
|
||||
#endif
|
||||
+57
-21
@@ -109,7 +109,9 @@ struct demux_internal {
|
||||
bool warned_queue_overflow;
|
||||
bool last_eof; // last actual global EOF status
|
||||
bool eof; // whether we're in EOF state (reset for retry)
|
||||
bool idle;
|
||||
bool autoselect;
|
||||
double min_secs;
|
||||
int min_packs;
|
||||
int min_bytes;
|
||||
|
||||
@@ -137,10 +139,16 @@ struct demux_stream {
|
||||
bool eof; // end of demuxed stream? (true if all buffer empty)
|
||||
size_t packs; // number of packets in buffer
|
||||
size_t bytes; // total bytes of packets in buffer
|
||||
double base_ts; // timestamp of the last packet returned to decoder
|
||||
double last_ts; // timestamp of the last packet added to queue
|
||||
struct demux_packet *head;
|
||||
struct demux_packet *tail;
|
||||
};
|
||||
|
||||
// If one of the values is NOPTS, always pick the other one.
|
||||
#define MP_PTS_MIN(a, b) ((a) == MP_NOPTS_VALUE || ((a) > (b)) ? (b) : (a))
|
||||
#define MP_PTS_MAX(a, b) ((a) == MP_NOPTS_VALUE || ((a) < (b)) ? (b) : (a))
|
||||
|
||||
static void demuxer_sort_chapters(demuxer_t *demuxer);
|
||||
static void *demux_thread(void *pctx);
|
||||
static void update_cache(struct demux_internal *in);
|
||||
@@ -157,6 +165,7 @@ static void ds_flush(struct demux_stream *ds)
|
||||
ds->head = ds->tail = NULL;
|
||||
ds->packs = 0;
|
||||
ds->bytes = 0;
|
||||
ds->last_ts = ds->base_ts = MP_NOPTS_VALUE;
|
||||
ds->eof = false;
|
||||
ds->active = false;
|
||||
}
|
||||
@@ -294,6 +303,7 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
// first packet in stream
|
||||
ds->head = ds->tail = dp;
|
||||
}
|
||||
|
||||
// obviously not true anymore
|
||||
ds->eof = false;
|
||||
in->last_eof = in->eof = false;
|
||||
@@ -303,11 +313,17 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
if (stream->type != STREAM_VIDEO && dp->pts == MP_NOPTS_VALUE)
|
||||
dp->pts = dp->dts;
|
||||
|
||||
double ts = dp->dts == MP_NOPTS_VALUE ? dp->pts : dp->dts;
|
||||
if (ts != MP_NOPTS_VALUE && (ts > ds->last_ts || ts + 10 < ds->last_ts))
|
||||
ds->last_ts = ts;
|
||||
if (ds->base_ts == MP_NOPTS_VALUE)
|
||||
ds->base_ts = ds->last_ts;
|
||||
|
||||
MP_DBG(in, "append packet to %s: size=%d pts=%f dts=%f pos=%"PRIi64" "
|
||||
"[num=%zd size=%zd]\n", stream_type_name(stream->type),
|
||||
dp->len, dp->pts, dp->dts, dp->pos, ds->packs, ds->bytes);
|
||||
|
||||
if (ds->in->wakeup_cb)
|
||||
if (ds->in->wakeup_cb && !ds->head->next)
|
||||
ds->in->wakeup_cb(ds->in->wakeup_cb_ctx);
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
@@ -318,6 +334,7 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
|
||||
static bool read_packet(struct demux_internal *in)
|
||||
{
|
||||
in->eof = false;
|
||||
in->idle = true;
|
||||
|
||||
// Check if we need to read a new packet. We do this if all queues are below
|
||||
// the minimum, or if a stream explicitly needs new packets. Also includes
|
||||
@@ -326,10 +343,12 @@ static bool read_packet(struct demux_internal *in)
|
||||
size_t packs = 0, bytes = 0;
|
||||
for (int n = 0; n < in->d_buffer->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_buffer->streams[n]->ds;
|
||||
active |= ds->selected && ds->active;
|
||||
active |= ds->active;
|
||||
read_more |= ds->active && !ds->head;
|
||||
packs += ds->packs;
|
||||
bytes += ds->bytes;
|
||||
if (ds->active && ds->last_ts != MP_NOPTS_VALUE && in->min_secs > 0)
|
||||
read_more |= ds->last_ts - ds->base_ts < in->min_secs;
|
||||
}
|
||||
MP_DBG(in, "packets=%zd, bytes=%zd, active=%d, more=%d\n",
|
||||
packs, bytes, active, read_more);
|
||||
@@ -360,6 +379,7 @@ static bool read_packet(struct demux_internal *in)
|
||||
|
||||
// Actually read a packet. Drop the lock while doing so, because waiting
|
||||
// for disk or network I/O can take time.
|
||||
in->idle = false;
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
struct demuxer *demux = in->d_thread;
|
||||
bool eof = !demux->desc->fill_buffer || demux->desc->fill_buffer(demux) <= 0;
|
||||
@@ -475,8 +495,12 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
|
||||
ds->bytes -= pkt->len;
|
||||
ds->packs--;
|
||||
|
||||
double ts = pkt->dts == MP_NOPTS_VALUE ? pkt->pts : pkt->dts;
|
||||
if (ts != MP_NOPTS_VALUE)
|
||||
ds->base_ts = ts;
|
||||
|
||||
// This implies this function is actually called from "the" user thread.
|
||||
if (pkt && pkt->pos >= 0)
|
||||
if (pkt->pos >= ds->in->d_user->filepos)
|
||||
ds->in->d_user->filepos = pkt->pos;
|
||||
|
||||
return pkt;
|
||||
@@ -558,18 +582,6 @@ bool demux_has_packet(struct sh_stream *sh)
|
||||
return has_packet;
|
||||
}
|
||||
|
||||
// Return whether EOF was returned with an earlier packet read.
|
||||
bool demux_stream_eof(struct sh_stream *sh)
|
||||
{
|
||||
bool eof = false;
|
||||
if (sh) {
|
||||
pthread_mutex_lock(&sh->ds->in->lock);
|
||||
eof = sh->ds->eof && !sh->ds->head;
|
||||
pthread_mutex_unlock(&sh->ds->in->lock);
|
||||
}
|
||||
return eof;
|
||||
}
|
||||
|
||||
// Read and return any packet we find.
|
||||
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
|
||||
{
|
||||
@@ -803,6 +815,8 @@ static struct demuxer *open_given_type(struct mpv_global *global,
|
||||
.d_thread = talloc(demuxer, struct demuxer),
|
||||
.d_buffer = talloc(demuxer, struct demuxer),
|
||||
.d_user = demuxer,
|
||||
.min_secs = stream->uncached_stream ? demuxer->opts->demuxer_min_secs_cache
|
||||
: demuxer->opts->demuxer_min_secs,
|
||||
.min_packs = demuxer->opts->demuxer_min_packs,
|
||||
.min_bytes = demuxer->opts->demuxer_min_bytes,
|
||||
};
|
||||
@@ -911,6 +925,8 @@ static void flush_locked(demuxer_t *demuxer)
|
||||
demuxer->in->warned_queue_overflow = false;
|
||||
demuxer->in->eof = false;
|
||||
demuxer->in->last_eof = false;
|
||||
demuxer->in->idle = true;
|
||||
demuxer->filepos = -1; // implicitly synchronized
|
||||
}
|
||||
|
||||
// clear the packet queues
|
||||
@@ -950,11 +966,6 @@ int demux_seek(demuxer_t *demuxer, float rel_seek_secs, int flags)
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *demux_info_get(demuxer_t *demuxer, const char *opt)
|
||||
{
|
||||
return mp_tags_get_str(demuxer->metadata, opt);
|
||||
}
|
||||
|
||||
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
|
||||
enum stream_type t, int id)
|
||||
{
|
||||
@@ -1106,6 +1117,10 @@ static void update_cache(struct demux_internal *in)
|
||||
// must be called locked
|
||||
static int cached_stream_control(struct demux_internal *in, int cmd, void *arg)
|
||||
{
|
||||
// If the cache is active, wake up the thread to possibly update cache state.
|
||||
if (in->stream_cache_size >= 0)
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
|
||||
switch (cmd) {
|
||||
case STREAM_CTRL_GET_CACHE_SIZE:
|
||||
if (in->stream_cache_size < 0)
|
||||
@@ -1150,7 +1165,29 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
|
||||
}
|
||||
case DEMUXER_CTRL_SWITCHED_TRACKS:
|
||||
in->tracks_switched = true;
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
return DEMUXER_CTRL_OK;
|
||||
case DEMUXER_CTRL_GET_READER_STATE: {
|
||||
struct demux_ctrl_reader_state *r = arg;
|
||||
*r = (struct demux_ctrl_reader_state){
|
||||
.eof = in->last_eof,
|
||||
.ts_range = {MP_NOPTS_VALUE, MP_NOPTS_VALUE},
|
||||
.ts_duration = -1,
|
||||
};
|
||||
for (int n = 0; n < in->d_user->num_streams; n++) {
|
||||
struct demux_stream *ds = in->d_user->streams[n]->ds;
|
||||
if (ds->active) {
|
||||
r->underrun |= !ds->head;
|
||||
r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
|
||||
r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
|
||||
}
|
||||
}
|
||||
r->idle = (in->idle && !r->underrun) || r->eof;
|
||||
r->underrun &= !r->idle;
|
||||
if (r->ts_range[0] != MP_NOPTS_VALUE && r->ts_range[1] != MP_NOPTS_VALUE)
|
||||
r->ts_duration = r->ts_range[1] - r->ts_range[0];
|
||||
return DEMUXER_CTRL_OK;
|
||||
}
|
||||
}
|
||||
return DEMUXER_CTRL_DONTKNOW;
|
||||
}
|
||||
@@ -1161,7 +1198,6 @@ int demux_control(demuxer_t *demuxer, int cmd, void *arg)
|
||||
|
||||
if (in->threading) {
|
||||
pthread_mutex_lock(&in->lock);
|
||||
pthread_cond_signal(&in->wakeup);
|
||||
int cr = cached_demux_control(in, cmd, arg);
|
||||
pthread_mutex_unlock(&in->lock);
|
||||
if (cr != DEMUXER_CTRL_DONTKNOW)
|
||||
|
||||
+8
-9
@@ -25,7 +25,7 @@
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "common/common.h"
|
||||
#include "common/tags.h"
|
||||
#include "packet.h"
|
||||
@@ -35,11 +35,6 @@
|
||||
// and the demuxer pretends EOF was reached.
|
||||
#define MAX_PACKS 16000
|
||||
#define MAX_PACK_BYTES (400 * 1024 * 1024)
|
||||
// Minimum total size of packets queued - the demuxer thread will read more
|
||||
// packets, until either number or total size of the packets exceed the minimum.
|
||||
// This can actually be configured with command line options.
|
||||
#define MIN_PACKS 64
|
||||
#define MIN_PACK_BYTES (5 * 1024 * 1024)
|
||||
|
||||
enum demuxer_type {
|
||||
DEMUXER_TYPE_GENERIC = 0,
|
||||
@@ -59,6 +54,13 @@ enum demux_ctrl {
|
||||
DEMUXER_CTRL_RESYNC,
|
||||
DEMUXER_CTRL_IDENTIFY_PROGRAM,
|
||||
DEMUXER_CTRL_STREAM_CTRL,
|
||||
DEMUXER_CTRL_GET_READER_STATE,
|
||||
};
|
||||
|
||||
struct demux_ctrl_reader_state {
|
||||
bool eof, underrun, idle;
|
||||
double ts_range[2]; // start, end
|
||||
double ts_duration;
|
||||
};
|
||||
|
||||
struct demux_ctrl_stream_ctrl {
|
||||
@@ -239,7 +241,6 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
|
||||
bool demux_stream_is_selected(struct sh_stream *stream);
|
||||
double demux_get_next_pts(struct sh_stream *sh);
|
||||
bool demux_has_packet(struct sh_stream *sh);
|
||||
bool demux_stream_eof(struct sh_stream *sh);
|
||||
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
|
||||
|
||||
struct sh_stream *new_sh_stream(struct demuxer *demuxer, enum stream_type type);
|
||||
@@ -255,8 +256,6 @@ void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *c
|
||||
void demux_flush(struct demuxer *demuxer);
|
||||
int demux_seek(struct demuxer *demuxer, float rel_seek_secs, int flags);
|
||||
|
||||
char *demux_info_get(struct demuxer *demuxer, const char *opt);
|
||||
|
||||
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
|
||||
|
||||
void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "demux.h"
|
||||
#include "stream/stream.h"
|
||||
|
||||
|
||||
+12
-2
@@ -135,20 +135,24 @@ static void add_streams(demuxer_t *demuxer)
|
||||
}
|
||||
}
|
||||
struct sh_stream *sh = new_sh_stream(demuxer, src->type);
|
||||
if (!sh)
|
||||
break;
|
||||
assert(p->num_streams == n); // directly mapped
|
||||
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
|
||||
// Copy all stream fields that might be relevant
|
||||
sh->codec = talloc_strdup(sh, src->codec);
|
||||
sh->format = src->format;
|
||||
sh->lav_headers = src->lav_headers;
|
||||
if (sh && src->video) {
|
||||
sh->demuxer_id = src->demuxer_id;
|
||||
if (src->video) {
|
||||
double ar;
|
||||
if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
|
||||
== STREAM_OK)
|
||||
sh->video->aspect = ar;
|
||||
}
|
||||
if (sh && src->audio)
|
||||
if (src->audio)
|
||||
sh->audio = src->audio;
|
||||
get_disc_lang(demuxer->stream, sh);
|
||||
}
|
||||
reselect_streams(demuxer);
|
||||
}
|
||||
@@ -308,6 +312,12 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
|
||||
// Can be seekable even if the stream isn't.
|
||||
demuxer->seekable = true;
|
||||
|
||||
// With cache enabled, the stream can be seekable. This causes demux_lavf.c
|
||||
// (actually libavformat/mpegts.c) to seek sometimes when reading a packet.
|
||||
// It does this to seek back a bit in case the current file position points
|
||||
// into the middle of a packet.
|
||||
demuxer->stream->seekable = false;
|
||||
|
||||
add_dvd_streams(demuxer);
|
||||
add_streams(demuxer);
|
||||
add_stream_chapters(demuxer);
|
||||
|
||||
+70
-39
@@ -39,13 +39,12 @@
|
||||
# include <libavutil/display.h>
|
||||
#endif
|
||||
#include <libavutil/opt.h>
|
||||
#include "compat/libav.h"
|
||||
|
||||
#include "options/options.h"
|
||||
#include "common/msg.h"
|
||||
#include "common/tags.h"
|
||||
#include "common/av_common.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "demux.h"
|
||||
@@ -132,6 +131,7 @@ static const char *const format_blacklist[] = {
|
||||
// Image demuxers, disabled in favor of demux_mf:
|
||||
"image2", "image2pipe",
|
||||
"bmp_pipe", "dpx_pipe", "exr_pipe", "j2k_pipe", "png_pipe", "tiff_pipe",
|
||||
"jpeg_pipe",
|
||||
0
|
||||
};
|
||||
|
||||
@@ -234,7 +234,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
|
||||
priv->filename = remove_prefix(priv->filename, prefixes);
|
||||
|
||||
char *avdevice_format = NULL;
|
||||
if (s->type == STREAMTYPE_AVDEVICE) {
|
||||
if (s->uncached_type == STREAMTYPE_AVDEVICE) {
|
||||
// always require filename in the form "format:filename"
|
||||
char *sep = strchr(priv->filename, ':');
|
||||
if (!sep) {
|
||||
@@ -430,6 +430,19 @@ static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return a dictionary entry as (decimal) integer.
|
||||
static int dict_get_decimal(AVDictionary *dict, const char *entry, int def)
|
||||
{
|
||||
AVDictionaryEntry *e = av_dict_get(dict, entry, NULL, 0);
|
||||
if (e && e->value) {
|
||||
char *end = NULL;
|
||||
long int r = strtol(e->value, &end, 10);
|
||||
if (end && !end[0] && r >= INT_MIN && r <= INT_MAX)
|
||||
return r;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
static void handle_stream(demuxer_t *demuxer, int i)
|
||||
{
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
@@ -448,7 +461,7 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
sh->format = codec->codec_tag;
|
||||
|
||||
// probably unneeded
|
||||
mp_chmap_from_channels(&sh_audio->channels, codec->channels);
|
||||
mp_chmap_set_unknown(&sh_audio->channels, codec->channels);
|
||||
if (codec->channel_layout)
|
||||
mp_chmap_from_lavc(&sh_audio->channels, codec->channel_layout);
|
||||
sh_audio->samplerate = codec->sample_rate;
|
||||
@@ -467,9 +480,11 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
|
||||
sh->attached_picture = new_demux_packet_from(st->attached_pic.data,
|
||||
st->attached_pic.size);
|
||||
sh->attached_picture->pts = 0;
|
||||
talloc_steal(sh, sh->attached_picture);
|
||||
sh->attached_picture->keyframe = true;
|
||||
if (sh->attached_picture) {
|
||||
sh->attached_picture->pts = 0;
|
||||
talloc_steal(sh, sh->attached_picture);
|
||||
sh->attached_picture->keyframe = true;
|
||||
}
|
||||
}
|
||||
|
||||
sh->format = codec->codec_tag;
|
||||
@@ -506,13 +521,9 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
if (sd)
|
||||
sh_video->rotate = -av_display_rotation_get((uint32_t *)sd);
|
||||
#else
|
||||
AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0);
|
||||
if (rot && rot->value) {
|
||||
char *end = NULL;
|
||||
long int r = strtol(rot->value, &end, 10);
|
||||
if (end && !end[0])
|
||||
sh_video->rotate = r;
|
||||
}
|
||||
int rot = dict_get_decimal(st->metadata, "rotate", -1);
|
||||
if (rot >= 0)
|
||||
sh_video->rotate = rot;
|
||||
#endif
|
||||
sh_video->rotate = ((sh_video->rotate % 360) + 360) % 360;
|
||||
|
||||
@@ -590,6 +601,9 @@ static void handle_stream(demuxer_t *demuxer, int i)
|
||||
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
|
||||
if (lang && lang->value)
|
||||
sh->lang = talloc_strdup(sh, lang->value);
|
||||
sh->hls_bitrate = dict_get_decimal(st->metadata, "variant_bitrate", 0);
|
||||
if (!sh->title && sh->hls_bitrate > 0)
|
||||
sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
|
||||
}
|
||||
|
||||
select_tracks(demuxer, i);
|
||||
@@ -606,10 +620,29 @@ static void add_new_streams(demuxer_t *demuxer)
|
||||
|
||||
static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
|
||||
{
|
||||
#if HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA
|
||||
#if HAVE_AVFORMAT_METADATA_UPDATE_FLAG
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
if (priv->avfc->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->metadata);
|
||||
priv->avfc->event_flags = 0;
|
||||
demux_changed(demuxer, DEMUX_EVENT_METADATA);
|
||||
}
|
||||
if (priv->merge_track_metadata) {
|
||||
for (int n = 0; n < priv->num_streams; n++) {
|
||||
AVStream *st = priv->streams[n] ? priv->avfc->streams[n] : NULL;
|
||||
if (st && st->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, st->metadata);
|
||||
st->event_flags = 0;
|
||||
demux_changed(demuxer, DEMUX_EVENT_METADATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA
|
||||
lavf_priv_t *priv = demuxer->priv;
|
||||
int md_size;
|
||||
const uint8_t *md;
|
||||
if (!pkt)
|
||||
return;
|
||||
md = av_packet_get_side_data(pkt, AV_PKT_DATA_METADATA_UPDATE, &md_size);
|
||||
if (md && priv->merge_track_metadata) {
|
||||
AVDictionary *dict = NULL;
|
||||
@@ -740,8 +773,6 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
add_new_streams(demuxer);
|
||||
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
|
||||
|
||||
// Often useful with OGG audio-only files, which have metadata in the audio
|
||||
// track metadata instead of the main metadata.
|
||||
if (demuxer->num_streams == 1) {
|
||||
@@ -752,6 +783,9 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
}
|
||||
}
|
||||
|
||||
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
|
||||
update_metadata(demuxer, NULL);
|
||||
|
||||
demuxer->ts_resets_possible = priv->avif->flags & AVFMT_TS_DISCONT;
|
||||
|
||||
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
|
||||
@@ -760,23 +794,21 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_avpacket(void *pkt)
|
||||
{
|
||||
av_free_packet(pkt);
|
||||
}
|
||||
|
||||
static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
{
|
||||
lavf_priv_t *priv = demux->priv;
|
||||
demux_packet_t *dp;
|
||||
|
||||
AVPacket *pkt = talloc(NULL, AVPacket);
|
||||
AVPacket *pkt = &(AVPacket){0};
|
||||
int r = av_read_frame(priv->avfc, pkt);
|
||||
if (r < 0) {
|
||||
talloc_free(pkt);
|
||||
return r == AVERROR(EAGAIN) ? 1 : -1; // eof
|
||||
av_free_packet(pkt);
|
||||
if (r == AVERROR(EAGAIN))
|
||||
return 1;
|
||||
if (r == AVERROR_EOF)
|
||||
return 0;
|
||||
MP_WARN(demux, "error reading packet.\n");
|
||||
return -1;
|
||||
}
|
||||
talloc_set_destructor(pkt, destroy_avpacket);
|
||||
|
||||
add_new_streams(demux);
|
||||
update_metadata(demux, pkt);
|
||||
@@ -786,18 +818,15 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
|
||||
AVStream *st = priv->avfc->streams[pkt->stream_index];
|
||||
|
||||
if (!demux_stream_is_selected(stream)) {
|
||||
talloc_free(pkt);
|
||||
av_free_packet(pkt);
|
||||
return 1; // don't signal EOF if skipping a packet
|
||||
}
|
||||
|
||||
// If the packet has pointers to temporary fields that could be
|
||||
// overwritten/freed by next av_read_frame(), copy them to persistent
|
||||
// allocations so we can safely queue the packet for any length of time.
|
||||
if (av_dup_packet(pkt) < 0)
|
||||
abort();
|
||||
|
||||
dp = new_demux_packet_fromdata(pkt->data, pkt->size);
|
||||
dp->avpacket = talloc_steal(dp, pkt);
|
||||
struct demux_packet *dp = new_demux_packet_from_avpacket(pkt);
|
||||
if (!dp) {
|
||||
av_free_packet(pkt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pkt->pts != AV_NOPTS_VALUE)
|
||||
dp->pts = pkt->pts * av_q2d(st->time_base);
|
||||
@@ -813,6 +842,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);
|
||||
demux_add_packet(stream, dp);
|
||||
return 1;
|
||||
}
|
||||
@@ -981,12 +1011,13 @@ redo:
|
||||
* call the new API instead of relying on av_seek_frame() to do this
|
||||
* for us.
|
||||
*/
|
||||
av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream),
|
||||
AVSEEK_FLAG_BYTE);
|
||||
// avio_flush() is designed for write-only streams, and does the wrong
|
||||
// thing when reading. Flush it manually instead.
|
||||
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end;
|
||||
stream_drop_buffers(demuxer->stream);
|
||||
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end = priv->avfc->pb->buffer;
|
||||
priv->avfc->pb->pos = stream_tell(demuxer->stream);
|
||||
av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream),
|
||||
AVSEEK_FLAG_BYTE);
|
||||
return DEMUXER_CTRL_OK;
|
||||
default:
|
||||
return DEMUXER_CTRL_NOTIMPL;
|
||||
|
||||
+6
-4
@@ -77,10 +77,12 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer)
|
||||
bstr data = stream_read_complete(stream, NULL, MF_MAX_FILE_SIZE);
|
||||
if (data.len) {
|
||||
demux_packet_t *dp = new_demux_packet(data.len);
|
||||
memcpy(dp->buffer, data.start, data.len);
|
||||
dp->pts = mf->curr_frame / mf->sh->fps;
|
||||
dp->keyframe = true;
|
||||
demux_add_packet(demuxer->streams[0], dp);
|
||||
if (dp) {
|
||||
memcpy(dp->buffer, data.start, data.len);
|
||||
dp->pts = mf->curr_frame / mf->sh->fps;
|
||||
dp->keyframe = true;
|
||||
demux_add_packet(demuxer->streams[0], dp);
|
||||
}
|
||||
}
|
||||
talloc_free(data.start);
|
||||
}
|
||||
|
||||
+132
-140
@@ -44,13 +44,15 @@
|
||||
#include "talloc.h"
|
||||
#include "common/av_common.h"
|
||||
#include "options/options.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
#include "stream/stream.h"
|
||||
#include "video/csputils.h"
|
||||
#include "demux.h"
|
||||
#include "stheader.h"
|
||||
#include "ebml.h"
|
||||
#include "matroska.h"
|
||||
#include "codec_tags.h"
|
||||
#include "video/img_fourcc.h"
|
||||
|
||||
#include "common/msg.h"
|
||||
|
||||
@@ -102,6 +104,7 @@ typedef struct mkv_track {
|
||||
bool v_dwidth_set, v_dheight_set;
|
||||
double v_frate;
|
||||
uint32_t colorspace;
|
||||
int stereo_mode;
|
||||
|
||||
uint32_t a_formattag;
|
||||
uint32_t a_channels, a_bps;
|
||||
@@ -504,6 +507,15 @@ static void parse_trackvideo(struct demuxer *demuxer, struct mkv_track *track,
|
||||
MP_VERBOSE(demuxer, "| + Colorspace: %#x\n",
|
||||
(unsigned int)track->colorspace);
|
||||
}
|
||||
if (video->n_stereo_mode) {
|
||||
const char *name = MP_STEREO3D_NAME(video->stereo_mode);
|
||||
if (name) {
|
||||
track->stereo_mode = video->stereo_mode;
|
||||
MP_VERBOSE(demuxer, "| + StereoMode: %s\n", name);
|
||||
} else {
|
||||
MP_WARN(demuxer, "Unknown StereoMode: %d\n", (int)video->stereo_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,7 +588,7 @@ static void parse_trackentry(struct demuxer *demuxer,
|
||||
track->codec_id = "";
|
||||
}
|
||||
|
||||
if (entry->n_codec_private && entry->codec_private.len <= 0x1000000) {
|
||||
if (entry->n_codec_private && entry->codec_private.len <= 0x10000000) {
|
||||
int len = entry->codec_private.len;
|
||||
track->private_data = talloc_size(track, len + AV_LZO_INPUT_PADDING);
|
||||
memcpy(track->private_data, entry->codec_private.start, len);
|
||||
@@ -1137,45 +1149,34 @@ static const videocodec_info_t vinfo[] = {
|
||||
|
||||
static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
{
|
||||
MP_BITMAPINFOHEADER *bih = &(MP_BITMAPINFOHEADER){0};
|
||||
unsigned char *extradata;
|
||||
unsigned int extradata_size = 0;
|
||||
struct sh_stream *sh;
|
||||
sh_video_t *sh_v;
|
||||
bool raw = false;
|
||||
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
|
||||
if (!sh)
|
||||
return 1;
|
||||
track->stream = sh;
|
||||
sh_video_t *sh_v = sh->video;
|
||||
sh->demuxer_id = track->tnum;
|
||||
sh->title = talloc_strdup(sh_v, track->name);
|
||||
|
||||
if (track->ms_compat) { /* MS compatibility mode */
|
||||
MP_BITMAPINFOHEADER *src;
|
||||
|
||||
if (track->private_data == NULL
|
||||
|| track->private_size < sizeof(*bih))
|
||||
// The private_data contains a BITMAPINFOHEADER struct
|
||||
if (track->private_data == NULL || track->private_size < 40)
|
||||
return 1;
|
||||
|
||||
src = (MP_BITMAPINFOHEADER *) track->private_data;
|
||||
bih->biSize = le2me_32(src->biSize);
|
||||
bih->biWidth = le2me_32(src->biWidth);
|
||||
bih->biHeight = le2me_32(src->biHeight);
|
||||
bih->biPlanes = le2me_16(src->biPlanes);
|
||||
bih->biBitCount = le2me_16(src->biBitCount);
|
||||
bih->biCompression = le2me_32(src->biCompression);
|
||||
bih->biSizeImage = le2me_32(src->biSizeImage);
|
||||
bih->biXPelsPerMeter = le2me_32(src->biXPelsPerMeter);
|
||||
bih->biYPelsPerMeter = le2me_32(src->biYPelsPerMeter);
|
||||
bih->biClrUsed = le2me_32(src->biClrUsed);
|
||||
bih->biClrImportant = le2me_32(src->biClrImportant);
|
||||
extradata = track->private_data + sizeof(*bih);
|
||||
extradata_size = track->private_size - sizeof(*bih);
|
||||
|
||||
unsigned char *h = track->private_data;
|
||||
if (track->v_width == 0)
|
||||
track->v_width = bih->biWidth;
|
||||
track->v_width = AV_RL32(h + 4); // biWidth
|
||||
if (track->v_height == 0)
|
||||
track->v_height = bih->biHeight;
|
||||
track->v_height = AV_RL32(h + 8); // biHeight
|
||||
sh_v->bits_per_coded_sample = AV_RL16(h + 14); // biBitCount
|
||||
sh->format = AV_RL32(h + 16); // biCompression
|
||||
|
||||
extradata = track->private_data + 40;
|
||||
extradata_size = track->private_size - 40;
|
||||
} else {
|
||||
bih->biSize = sizeof(*bih);
|
||||
bih->biWidth = track->v_width;
|
||||
bih->biHeight = track->v_height;
|
||||
bih->biBitCount = 24;
|
||||
bih->biSizeImage = bih->biWidth * bih->biHeight * bih->biBitCount / 8;
|
||||
sh_v->bits_per_coded_sample = 24;
|
||||
|
||||
if (track->private_size >= RVPROPERTIES_SIZE
|
||||
&& (!strcmp(track->codec_id, MKV_V_REALV10)
|
||||
@@ -1189,30 +1190,26 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
src = (uint8_t *) track->private_data + RVPROPERTIES_SIZE;
|
||||
|
||||
cnt = track->private_size - RVPROPERTIES_SIZE;
|
||||
bih->biSize = 48 + cnt;
|
||||
bih->biPlanes = 1;
|
||||
type2 = AV_RB32(src - 4);
|
||||
if (type2 == 0x10003000 || type2 == 0x10003001)
|
||||
bih->biCompression = MP_FOURCC('R', 'V', '1', '3');
|
||||
sh->format = MP_FOURCC('R', 'V', '1', '3');
|
||||
else
|
||||
bih->biCompression =
|
||||
MP_FOURCC('R', 'V', track->codec_id[9], '0');
|
||||
sh->format = MP_FOURCC('R', 'V', track->codec_id[9], '0');
|
||||
// copy type1 and type2 info from rv properties
|
||||
extradata_size = cnt + 8;
|
||||
extradata = src - 8;
|
||||
track->realmedia = 1;
|
||||
} else if (strcmp(track->codec_id, MKV_V_UNCOMPRESSED) == 0) {
|
||||
// raw video, "like AVI" - this is a FourCC
|
||||
bih->biCompression = track->colorspace;
|
||||
sh->format = track->colorspace;
|
||||
raw = true;
|
||||
} else {
|
||||
const videocodec_info_t *vi = vinfo;
|
||||
while (vi->id && strcmp(vi->id, track->codec_id))
|
||||
vi++;
|
||||
bih->biCompression = vi->fourcc;
|
||||
sh->format = vi->fourcc;
|
||||
if (vi->extradata && track->private_data
|
||||
&& (track->private_size > 0)) {
|
||||
bih->biSize += track->private_size;
|
||||
extradata = track->private_data;
|
||||
extradata_size = track->private_size;
|
||||
}
|
||||
@@ -1232,22 +1229,10 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
return 1;
|
||||
}
|
||||
|
||||
sh = new_sh_stream(demuxer, STREAM_VIDEO);
|
||||
if (!sh)
|
||||
return 1;
|
||||
track->stream = sh;
|
||||
sh_v = sh->video;
|
||||
sh->demuxer_id = track->tnum;
|
||||
sh->title = talloc_strdup(sh_v, track->name);
|
||||
sh_v->bih = talloc_size(sh_v, sizeof(MP_BITMAPINFOHEADER) + extradata_size);
|
||||
if (!sh_v->bih) {
|
||||
MP_FATAL(demuxer, "Memory allocation failure!\n");
|
||||
abort();
|
||||
}
|
||||
*sh_v->bih = *bih;
|
||||
if (extradata_size)
|
||||
memcpy(sh_v->bih + 1, extradata, extradata_size);
|
||||
sh->format = sh_v->bih->biCompression;
|
||||
sh_v->coded_width = track->v_width;
|
||||
sh_v->coded_height = track->v_height;
|
||||
sh_v->extradata = talloc_memdup(sh_v, extradata, extradata_size);
|
||||
sh_v->extradata_len = extradata_size;
|
||||
if (raw) {
|
||||
sh->codec = "rawvideo";
|
||||
} else {
|
||||
@@ -1276,6 +1261,7 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
MP_VERBOSE(demuxer, "Aspect: %f\n", sh_v->aspect);
|
||||
sh_v->avi_dts = track->ms_compat;
|
||||
sh_v->stereo_mode = track->stereo_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1318,15 +1304,6 @@ static const struct mkv_audio_tag {
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static void copy_audio_private_data(sh_audio_t *sh, mkv_track_t *track)
|
||||
{
|
||||
if (!track->ms_compat && track->private_size) {
|
||||
sh->codecdata = talloc_size(sh, track->private_size);
|
||||
sh->codecdata_len = track->private_size;
|
||||
memcpy(sh->codecdata, track->private_data, track->private_size);
|
||||
}
|
||||
}
|
||||
|
||||
static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
{
|
||||
struct sh_stream *sh = new_sh_stream(demuxer, STREAM_AUDIO);
|
||||
@@ -1335,6 +1312,9 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
track->stream = sh;
|
||||
sh_audio_t *sh_a = sh->audio;
|
||||
|
||||
unsigned char *extradata = NULL;
|
||||
int extradata_len = 0;
|
||||
|
||||
if (track->language && (strcmp(track->language, "und") != 0))
|
||||
sh->lang = talloc_strdup(sh_a, track->language);
|
||||
sh->demuxer_id = track->tnum;
|
||||
@@ -1342,30 +1322,25 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
sh->default_track = track->default_track;
|
||||
if (!track->a_osfreq)
|
||||
track->a_osfreq = track->a_sfreq;
|
||||
|
||||
if (track->ms_compat) {
|
||||
if (track->private_size < sizeof(*sh_a->wf))
|
||||
// The private_data contains a WAVEFORMATEX struct
|
||||
if (track->private_size < 18)
|
||||
goto error;
|
||||
MP_VERBOSE(demuxer, "track with MS compat audio.\n");
|
||||
MP_WAVEFORMATEX *wf = (MP_WAVEFORMATEX *) track->private_data;
|
||||
sh_a->wf = talloc_zero_size(sh_a, track->private_size);
|
||||
sh_a->wf->wFormatTag = le2me_16(wf->wFormatTag);
|
||||
sh_a->wf->nChannels = le2me_16(wf->nChannels);
|
||||
sh_a->wf->nSamplesPerSec = le2me_32(wf->nSamplesPerSec);
|
||||
sh_a->wf->nAvgBytesPerSec = le2me_32(wf->nAvgBytesPerSec);
|
||||
sh_a->wf->nBlockAlign = le2me_16(wf->nBlockAlign);
|
||||
sh_a->wf->wBitsPerSample = le2me_16(wf->wBitsPerSample);
|
||||
sh_a->wf->cbSize = track->private_size - sizeof(*sh_a->wf);
|
||||
memcpy(sh_a->wf + 1, wf + 1,
|
||||
track->private_size - sizeof(*sh_a->wf));
|
||||
if (track->a_osfreq == 0.0)
|
||||
track->a_osfreq = sh_a->wf->nSamplesPerSec;
|
||||
unsigned char *h = track->private_data;
|
||||
track->a_formattag = AV_RL16(h + 0); // wFormatTag
|
||||
if (track->a_channels == 0)
|
||||
track->a_channels = sh_a->wf->nChannels;
|
||||
track->a_channels = AV_RL16(h + 2); // nChannels
|
||||
if (track->a_osfreq == 0.0)
|
||||
track->a_osfreq = AV_RL32(h + 4); // nSamplesPerSec
|
||||
sh_a->bitrate = AV_RL32(h + 8) * 8; // nAvgBytesPerSec
|
||||
sh_a->block_align = AV_RL16(h + 12); // nBlockAlign
|
||||
if (track->a_bps == 0)
|
||||
track->a_bps = sh_a->wf->wBitsPerSample;
|
||||
track->a_formattag = sh_a->wf->wFormatTag;
|
||||
track->a_bps = AV_RL16(h + 14); // wBitsPerSample
|
||||
extradata = track->private_data + 18;
|
||||
extradata_len = track->private_size - 18;
|
||||
} else {
|
||||
sh_a->wf = talloc_zero(sh_a, MP_WAVEFORMATEX);
|
||||
for (int i = 0; ; i++) {
|
||||
const struct mkv_audio_tag *t = mkv_audio_tags + i;
|
||||
if (t->id == NULL)
|
||||
@@ -1384,23 +1359,21 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
|
||||
sh->format = track->a_formattag;
|
||||
sh_a->wf->wFormatTag = track->a_formattag;
|
||||
mp_chmap_from_channels(&sh_a->channels, track->a_channels);
|
||||
sh_a->wf->nChannels = track->a_channels;
|
||||
mp_chmap_set_unknown(&sh_a->channels, track->a_channels);
|
||||
sh_a->samplerate = (uint32_t) track->a_osfreq;
|
||||
sh_a->wf->nSamplesPerSec = (uint32_t) track->a_osfreq;
|
||||
if (track->a_bps == 0)
|
||||
sh_a->wf->wBitsPerSample = 16;
|
||||
sh_a->bits_per_coded_sample = 16;
|
||||
else
|
||||
sh_a->wf->wBitsPerSample = track->a_bps;
|
||||
sh_a->bits_per_coded_sample = track->a_bps;
|
||||
if (track->a_formattag == 0x0055) { /* MP3 || MP2 */
|
||||
sh_a->wf->nAvgBytesPerSec = 16000;
|
||||
sh_a->wf->nBlockAlign = 1152;
|
||||
sh_a->bitrate = 16000 * 8;
|
||||
sh_a->block_align = 1152;
|
||||
} else if ((track->a_formattag == 0x2000) /* AC3 */
|
||||
|| track->a_formattag == MP_FOURCC('E', 'A', 'C', '3')
|
||||
|| (track->a_formattag == 0x2001)) { /* DTS */
|
||||
talloc_free(sh_a->wf);
|
||||
sh_a->wf = NULL;
|
||||
sh_a->bits_per_coded_sample = 0;
|
||||
sh_a->bitrate = 0;
|
||||
sh_a->block_align = 0;
|
||||
} else if (track->a_formattag == 0x0001) { /* PCM || PCM_BE */
|
||||
if (!strcmp(track->codec_id, MKV_A_PCM_BE))
|
||||
sh->format = MP_FOURCC('t', 'w', 'o', 's');
|
||||
@@ -1408,17 +1381,23 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
/* ok */
|
||||
} else if (!strcmp(track->codec_id, MKV_A_QDMC)
|
||||
|| !strcmp(track->codec_id, MKV_A_QDMC2)) {
|
||||
sh_a->wf->nAvgBytesPerSec = 16000;
|
||||
sh_a->wf->nBlockAlign = 1486;
|
||||
copy_audio_private_data(sh_a, track);
|
||||
sh_a->bitrate = 16000 * 8;
|
||||
sh_a->block_align = 1486;
|
||||
if (!extradata_len) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
} else if (track->a_formattag == MP_FOURCC('M', 'P', '4', 'A')) {
|
||||
int profile, srate_idx;
|
||||
|
||||
sh_a->wf->nAvgBytesPerSec = 16000;
|
||||
sh_a->wf->nBlockAlign = 1024;
|
||||
sh_a->bitrate = 16000 * 8;
|
||||
sh_a->block_align = 1024;
|
||||
|
||||
if (!strcmp(track->codec_id, MKV_A_AAC) && track->private_data) {
|
||||
copy_audio_private_data(sh_a, track);
|
||||
if (!extradata_len) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
} else {
|
||||
/* Recreate the 'private data' */
|
||||
/* which faad2 uses in its initialization */
|
||||
@@ -1440,7 +1419,6 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
/* HE-AAC (aka SBR AAC) */
|
||||
sh_a->codecdata_len = 5;
|
||||
|
||||
sh_a->samplerate = sh_a->wf->nSamplesPerSec = track->a_osfreq;
|
||||
srate_idx = aac_get_sample_rate_index(sh_a->samplerate);
|
||||
sh_a->codecdata[2] = AAC_SYNC_EXTENSION_TYPE >> 3;
|
||||
sh_a->codecdata[3] = ((AAC_SYNC_EXTENSION_TYPE & 0x07) << 5) | 5;
|
||||
@@ -1453,21 +1431,19 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
} else if (track->a_formattag == MP_FOURCC('v', 'r', 'b', 's')) {
|
||||
/* VORBIS */
|
||||
if (track->private_size == 0 || track->ms_compat && !sh_a->wf->cbSize)
|
||||
if (track->private_size == 0 || (track->ms_compat && !extradata_len))
|
||||
goto error;
|
||||
if (!track->ms_compat) {
|
||||
if (track->private_size > 0x1000000)
|
||||
goto error;
|
||||
sh_a->wf->cbSize = track->private_size;
|
||||
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
|
||||
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
|
||||
memcpy((unsigned char *) (sh_a->wf + 1), track->private_data,
|
||||
sh_a->wf->cbSize);
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
} else if (!strcmp(track->codec_id, MKV_A_OPUS)
|
||||
|| !strcmp(track->codec_id, MKV_A_OPUS_EXP)) {
|
||||
sh->format = MP_FOURCC('O', 'p', 'u', 's');
|
||||
copy_audio_private_data(sh_a, track);
|
||||
if (!track->ms_compat) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
} else if (!strncmp(track->codec_id, MKV_A_REALATRC, 7)) {
|
||||
if (track->private_size < RAPROPERTIES4_SIZE)
|
||||
goto error;
|
||||
@@ -1476,13 +1452,13 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
int codecdata_length, version;
|
||||
int flavor;
|
||||
|
||||
sh_a->wf->nAvgBytesPerSec = 0; /* FIXME !? */
|
||||
sh_a->bitrate = 0; /* FIXME !? */
|
||||
|
||||
version = AV_RB16(src + 4);
|
||||
flavor = AV_RB16(src + 22);
|
||||
track->coded_framesize = AV_RB32(src + 24);
|
||||
track->sub_packet_h = AV_RB16(src + 40);
|
||||
sh_a->wf->nBlockAlign = track->audiopk_size = AV_RB16(src + 42);
|
||||
sh_a->block_align = track->audiopk_size = AV_RB16(src + 42);
|
||||
track->sub_packet_size = AV_RB16(src + 44);
|
||||
if (version == 4) {
|
||||
src += RAPROPERTIES4_SIZE;
|
||||
@@ -1505,27 +1481,25 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
if (codecdata_length < 0 || codecdata_length > 0x1000000)
|
||||
goto error;
|
||||
src += 4;
|
||||
sh_a->wf->cbSize = codecdata_length;
|
||||
sh_a->wf = talloc_realloc_size(sh_a, sh_a->wf,
|
||||
sizeof(*sh_a->wf) + sh_a->wf->cbSize);
|
||||
memcpy(((char *) (sh_a->wf + 1)), src, codecdata_length);
|
||||
extradata_len = codecdata_length;
|
||||
extradata = src;
|
||||
|
||||
switch (track->a_formattag) {
|
||||
case MP_FOURCC('a', 't', 'r', 'c'):
|
||||
sh_a->wf->nAvgBytesPerSec = atrc_fl2bps[flavor];
|
||||
sh_a->wf->nBlockAlign = track->sub_packet_size;
|
||||
sh_a->bitrate = atrc_fl2bps[flavor] * 8;
|
||||
sh_a->block_align = track->sub_packet_size;
|
||||
goto audiobuf;
|
||||
case MP_FOURCC('c', 'o', 'o', 'k'):
|
||||
sh_a->wf->nAvgBytesPerSec = cook_fl2bps[flavor];
|
||||
sh_a->wf->nBlockAlign = track->sub_packet_size;
|
||||
sh_a->bitrate = cook_fl2bps[flavor] * 8;
|
||||
sh_a->block_align = track->sub_packet_size;
|
||||
goto audiobuf;
|
||||
case MP_FOURCC('s', 'i', 'p', 'r'):
|
||||
sh_a->wf->nAvgBytesPerSec = sipr_fl2bps[flavor];
|
||||
sh_a->wf->nBlockAlign = track->coded_framesize;
|
||||
sh_a->bitrate = sipr_fl2bps[flavor] * 8;
|
||||
sh_a->block_align = track->coded_framesize;
|
||||
goto audiobuf;
|
||||
case MP_FOURCC('2', '8', '_', '8'):
|
||||
sh_a->wf->nAvgBytesPerSec = 3600;
|
||||
sh_a->wf->nBlockAlign = track->coded_framesize;
|
||||
sh_a->bitrate = 3600 * 8;
|
||||
sh_a->block_align = track->coded_framesize;
|
||||
audiobuf:
|
||||
track->audio_buf =
|
||||
talloc_array_size(track, track->sub_packet_h, track->audiopk_size);
|
||||
@@ -1539,16 +1513,17 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
|| (track->a_formattag == 0xf1ac)) {
|
||||
unsigned char *ptr;
|
||||
int size;
|
||||
talloc_free(sh_a->wf);
|
||||
sh_a->wf = NULL;
|
||||
sh_a->bits_per_coded_sample = 0;
|
||||
sh_a->bitrate = 0;
|
||||
sh_a->block_align = 0;
|
||||
|
||||
if (!track->ms_compat) {
|
||||
ptr = track->private_data;
|
||||
size = track->private_size;
|
||||
} else {
|
||||
sh->format = MP_FOURCC('f', 'L', 'a', 'C');
|
||||
ptr = track->private_data + sizeof(*sh_a->wf);
|
||||
size = track->private_size - sizeof(*sh_a->wf);
|
||||
ptr = extradata;
|
||||
size = extradata_len;
|
||||
}
|
||||
if (size < 4 || ptr[0] != 'f' || ptr[1] != 'L' || ptr[2] != 'a'
|
||||
|| ptr[3] != 'C') {
|
||||
@@ -1572,7 +1547,10 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
}
|
||||
} else if (track->a_formattag == MP_FOURCC('W', 'V', 'P', 'K') ||
|
||||
track->a_formattag == MP_FOURCC('T', 'R', 'H', 'D')) {
|
||||
copy_audio_private_data(sh_a, track);
|
||||
if (!track->ms_compat) {
|
||||
extradata = track->private_data;
|
||||
extradata_len = track->private_size;
|
||||
}
|
||||
} else if (track->a_formattag == MP_FOURCC('T', 'T', 'A', '1')) {
|
||||
sh_a->codecdata_len = 30;
|
||||
sh_a->codecdata = talloc_zero_size(sh_a, sh_a->codecdata_len);
|
||||
@@ -1582,7 +1560,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
memcpy(data + 0, "TTA1", 4);
|
||||
AV_WL16(data + 4, 1);
|
||||
AV_WL16(data + 6, sh_a->channels.num);
|
||||
AV_WL16(data + 8, sh_a->wf->wBitsPerSample);
|
||||
AV_WL16(data + 8, sh_a->bits_per_coded_sample);
|
||||
AV_WL32(data + 10, track->a_osfreq);
|
||||
// Bogus: last frame won't be played.
|
||||
AV_WL32(data + 14, 0);
|
||||
@@ -1598,6 +1576,14 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
|
||||
|
||||
mp_set_codec_from_tag(sh);
|
||||
|
||||
if (track->private_size > 0x1000000 || extradata_len > 0x1000000)
|
||||
goto error;
|
||||
|
||||
if (!sh_a->codecdata && extradata) {
|
||||
sh_a->codecdata = talloc_memdup(sh_a, extradata, extradata_len);
|
||||
sh_a->codecdata_len = extradata_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@@ -1952,11 +1938,7 @@ static int64_t real_fix_timestamp(unsigned char *buf, int len, int64_t timestamp
|
||||
*kf_base = timestamp;
|
||||
*kf_pts = pts;
|
||||
} else {
|
||||
if (pict_type != 3) {
|
||||
timestamp = *kf_base + ((pts - *kf_pts) & 0x1FFF);
|
||||
} else {
|
||||
timestamp = *kf_base - ((*kf_pts - pts) & 0x1FFF);
|
||||
}
|
||||
timestamp = *kf_base - ((*kf_pts - pts) & 0x1FFF);
|
||||
}
|
||||
|
||||
return timestamp;
|
||||
@@ -1970,6 +1952,8 @@ static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
|
||||
int64_t timestamp = mkv_d->last_pts * 1000;
|
||||
|
||||
dp = new_demux_packet_from(data.start, data.len);
|
||||
if (!dp)
|
||||
return;
|
||||
|
||||
if (mkv_d->v_skip_to_keyframe) {
|
||||
dp->pts = mkv_d->last_pts;
|
||||
@@ -1978,7 +1962,7 @@ static void handle_realvideo(demuxer_t *demuxer, mkv_track_t *track,
|
||||
} else {
|
||||
dp->pts =
|
||||
real_fix_timestamp(dp->buffer, dp->len, timestamp,
|
||||
track->stream->video->bih->biCompression,
|
||||
track->stream->format,
|
||||
&track->rv_kf_base, &track->rv_kf_pts) * 0.001;
|
||||
}
|
||||
dp->pos = mkv_d->last_filepos;
|
||||
@@ -2081,13 +2065,15 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
|
||||
if (++(track->sub_packet_cnt) == sph) {
|
||||
track->sub_packet_cnt = 0;
|
||||
// apk_usize has same range as coded_framesize in worst case
|
||||
uint32_t apk_usize = track->stream->audio->wf->nBlockAlign;
|
||||
uint32_t apk_usize = track->stream->audio->block_align;
|
||||
if (apk_usize > audiobuf_size)
|
||||
goto error;
|
||||
// Release all the audio packets
|
||||
for (int x = 0; x < sph * w / apk_usize; x++) {
|
||||
dp = new_demux_packet_from(track->audio_buf + x * apk_usize,
|
||||
apk_usize);
|
||||
if (!dp)
|
||||
goto error;
|
||||
/* Put timestamp only on packets that correspond to original
|
||||
* audio packets in file */
|
||||
dp->pts = (x * apk_usize % w) ? MP_NOPTS_VALUE :
|
||||
@@ -2099,6 +2085,8 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
|
||||
}
|
||||
} else { // Not a codec that requires reordering
|
||||
dp = new_demux_packet_from(buffer, size);
|
||||
if (!dp)
|
||||
goto error;
|
||||
if (track->ra_pts == mkv_d->last_pts && !mkv_d->a_skip_to_keyframe)
|
||||
dp->pts = MP_NOPTS_VALUE;
|
||||
else
|
||||
@@ -2111,7 +2099,7 @@ static void handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
|
||||
}
|
||||
return;
|
||||
error:
|
||||
MP_ERR(demuxer, "RealAudio decrypting error.\n");
|
||||
MP_ERR(demuxer, "RealAudio packet extraction or decryption error.\n");
|
||||
}
|
||||
|
||||
static void mkv_seek_reset(demuxer_t *demuxer)
|
||||
@@ -2416,17 +2404,21 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
|
||||
while (raw.start && mkv_parse_packet(track, &raw, &buffer)) {
|
||||
demux_packet_t *dp =
|
||||
new_demux_packet_from(buffer.start, buffer.len);
|
||||
if (!dp)
|
||||
break;
|
||||
dp->keyframe = keyframe;
|
||||
/* If default_duration is 0, assume no pts value is known
|
||||
* for packets after the first one (rather than all pts
|
||||
* values being the same) */
|
||||
if (p == 0 || track->default_duration)
|
||||
* values being the same). Also, don't use it for extra
|
||||
* packets resulting from parsing. */
|
||||
if (p == 0 || (p == i && track->default_duration))
|
||||
dp->pts = mkv_d->last_pts + p * track->default_duration;
|
||||
p++;
|
||||
if (track->ms_compat)
|
||||
MPSWAP(double, dp->pts, dp->dts);
|
||||
dp->duration = block_duration / 1e9;
|
||||
if (p == 0)
|
||||
dp->duration = block_duration / 1e9;
|
||||
demux_add_packet(stream, dp);
|
||||
p++;
|
||||
}
|
||||
talloc_free_children(track->parser_tmp);
|
||||
}
|
||||
|
||||
+44
-18
@@ -29,33 +29,62 @@
|
||||
#include "stream/stream.h"
|
||||
#include "demux.h"
|
||||
#include "stheader.h"
|
||||
#include "audio/format.h"
|
||||
#include "codec_tags.h"
|
||||
|
||||
#include "video/img_format.h"
|
||||
#include "video/img_fourcc.h"
|
||||
|
||||
#include "osdep/endian.h"
|
||||
|
||||
struct demux_rawaudio_opts {
|
||||
struct mp_chmap channels;
|
||||
int samplerate;
|
||||
int aformat;
|
||||
};
|
||||
|
||||
// Ad-hoc schema to systematically encode the format as int
|
||||
#define PCM(sign, is_float, bits, is_be) \
|
||||
((sign) | ((is_float) << 1) | ((is_be) << 2) | ((bits) << 3))
|
||||
#define NE (BYTE_ORDER == BIG_ENDIAN)
|
||||
|
||||
#define OPT_BASE_STRUCT struct demux_rawaudio_opts
|
||||
const struct m_sub_options demux_rawaudio_conf = {
|
||||
.opts = (const m_option_t[]) {
|
||||
OPT_CHMAP("channels", channels, CONF_MIN, .min = 1),
|
||||
OPT_INTRANGE("rate", samplerate, 0, 1000, 8 * 48000),
|
||||
OPT_AUDIOFORMAT("format", aformat, 0),
|
||||
OPT_CHOICE("format", aformat, 0,
|
||||
({"u8", PCM(0, 0, 8, 0)},
|
||||
{"s8", PCM(1, 0, 8, 0)},
|
||||
{"u16le", PCM(0, 0, 16, 0)}, {"u16be", PCM(0, 0, 16, 1)},
|
||||
{"s16le", PCM(1, 0, 16, 0)}, {"u16be", PCM(1, 0, 16, 1)},
|
||||
{"u24le", PCM(0, 0, 24, 0)}, {"u24be", PCM(0, 0, 24, 1)},
|
||||
{"s24le", PCM(1, 0, 24, 0)}, {"s24be", PCM(1, 0, 24, 1)},
|
||||
{"u32le", PCM(0, 0, 32, 0)}, {"u32be", PCM(0, 0, 32, 1)},
|
||||
{"s32le", PCM(1, 0, 32, 0)}, {"s32be", PCM(1, 0, 32, 1)},
|
||||
{"floatle", PCM(0, 1, 32, 0)}, {"floatbe", PCM(0, 1, 32, 1)},
|
||||
{"doublele",PCM(0, 1, 64, 0)}, {"doublebe", PCM(0, 1, 64, 1)},
|
||||
{"u16", PCM(0, 0, 16, NE)},
|
||||
{"s16", PCM(1, 0, 16, NE)},
|
||||
{"u24", PCM(0, 0, 24, NE)},
|
||||
{"s24", PCM(1, 0, 24, NE)},
|
||||
{"u32", PCM(0, 0, 32, NE)},
|
||||
{"s32", PCM(1, 0, 32, NE)},
|
||||
{"float", PCM(0, 1, 32, NE)},
|
||||
{"double", PCM(0, 1, 64, NE)})),
|
||||
{0}
|
||||
},
|
||||
.size = sizeof(struct demux_rawaudio_opts),
|
||||
.defaults = &(const struct demux_rawaudio_opts){
|
||||
// Note that currently, stream_cdda expects exactly these parameters!
|
||||
.channels = MP_CHMAP_INIT_STEREO,
|
||||
.samplerate = 44100,
|
||||
.aformat = AF_FORMAT_S16,
|
||||
.aformat = PCM(1, 0, 16, 0), // s16le
|
||||
},
|
||||
};
|
||||
|
||||
#undef PCM
|
||||
#undef NE
|
||||
|
||||
struct demux_rawvideo_opts {
|
||||
int vformat;
|
||||
int mp_format;
|
||||
@@ -99,28 +128,20 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
|
||||
struct demux_rawaudio_opts *opts = demuxer->opts->demux_rawaudio;
|
||||
struct sh_stream *sh;
|
||||
sh_audio_t *sh_audio;
|
||||
MP_WAVEFORMATEX *w;
|
||||
|
||||
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
|
||||
return -1;
|
||||
|
||||
if ((opts->aformat & AF_FORMAT_SPECIAL_MASK) != 0)
|
||||
return -1;
|
||||
|
||||
sh = new_sh_stream(demuxer, STREAM_AUDIO);
|
||||
sh_audio = sh->audio;
|
||||
sh->codec = "mp-pcm";
|
||||
sh->format = opts->aformat;
|
||||
sh_audio->wf = w = talloc_zero(sh, MP_WAVEFORMATEX);
|
||||
w->wFormatTag = 0;
|
||||
sh_audio->channels = opts->channels;
|
||||
w->nChannels = sh_audio->channels.num;
|
||||
w->nSamplesPerSec = sh_audio->samplerate = opts->samplerate;
|
||||
int samplesize = af_fmt2bps(opts->aformat);
|
||||
w->nAvgBytesPerSec = sh_audio->samplerate * samplesize * w->nChannels;
|
||||
w->nBlockAlign = w->nChannels * samplesize;
|
||||
w->wBitsPerSample = 8 * samplesize;
|
||||
w->cbSize = 0;
|
||||
sh_audio->force_channels = true;
|
||||
sh_audio->samplerate = opts->samplerate;
|
||||
|
||||
int f = opts->aformat;
|
||||
// See PCM(): sign float bits endian
|
||||
mp_set_pcm_codec(sh, f & 1, f & 2, f >> 3, f & 4);
|
||||
int samplesize = ((f >> 3) + 7) / 8;
|
||||
|
||||
struct priv *p = talloc_ptrtype(demuxer, p);
|
||||
demuxer->priv = p;
|
||||
@@ -227,6 +248,11 @@ static int raw_fill_buffer(demuxer_t *demuxer)
|
||||
return 0;
|
||||
|
||||
struct demux_packet *dp = new_demux_packet(p->frame_size * p->read_frames);
|
||||
if (!dp) {
|
||||
MP_ERR(demuxer, "Can't read packet.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dp->pos = stream_tell(demuxer->stream);
|
||||
dp->pts = (dp->pos / p->frame_size) / p->frame_rate;
|
||||
|
||||
|
||||
+21
-38
@@ -8,9 +8,11 @@
|
||||
#include "options/options.h"
|
||||
|
||||
#include "demux.h"
|
||||
#include "codec_tags.h"
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "video/img_fourcc.h"
|
||||
#include "osdep/endian.h"
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "stream/tv.h"
|
||||
@@ -104,16 +106,9 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
|
||||
|
||||
switch(audio_format)
|
||||
{
|
||||
case AF_FORMAT_U8:
|
||||
case AF_FORMAT_S8:
|
||||
case AF_FORMAT_U16_LE:
|
||||
case AF_FORMAT_U16_BE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
case AF_FORMAT_S16_BE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
case AF_FORMAT_S32_BE:
|
||||
// This is the only format any of the current inputs generate.
|
||||
case AF_FORMAT_S16:
|
||||
break;
|
||||
case AF_FORMAT_MPEG2:
|
||||
default:
|
||||
MP_ERR(tvh, "Audio type '%s' unsupported!\n",
|
||||
af_fmt_to_str(audio_format));
|
||||
@@ -130,27 +125,11 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
|
||||
&nchannels);
|
||||
mp_chmap_from_channels(&sh_audio->channels, nchannels);
|
||||
|
||||
sh_a->codec = "mp-pcm";
|
||||
sh_a->format = audio_format;
|
||||
|
||||
int samplesize = af_fmt2bps(audio_format);
|
||||
int block_align = samplesize * sh_audio->channels.num;
|
||||
int bytes_per_second = sh_audio->samplerate * block_align;
|
||||
|
||||
sh_audio->bitrate = bytes_per_second * 8;
|
||||
|
||||
// emulate WF for win32 codecs:
|
||||
sh_audio->wf = talloc_zero(sh_audio, MP_WAVEFORMATEX);
|
||||
sh_audio->wf->wFormatTag = sh_a->format;
|
||||
sh_audio->wf->nChannels = sh_audio->channels.num;
|
||||
sh_audio->wf->wBitsPerSample = samplesize * 8;
|
||||
sh_audio->wf->nSamplesPerSec = sh_audio->samplerate;
|
||||
sh_audio->wf->nBlockAlign = block_align;
|
||||
sh_audio->wf->nAvgBytesPerSec = bytes_per_second;
|
||||
// s16ne
|
||||
mp_set_pcm_codec(sh_a, true, false, 16, BYTE_ORDER == BIG_ENDIAN);
|
||||
|
||||
MP_VERBOSE(tvh, " TV audio: %d channels, %d bits, %d Hz\n",
|
||||
sh_audio->wf->nChannels, sh_audio->wf->wBitsPerSample,
|
||||
sh_audio->wf->nSamplesPerSec);
|
||||
nchannels, 16, sh_audio->samplerate);
|
||||
}
|
||||
no_audio:
|
||||
|
||||
@@ -204,26 +183,30 @@ static int demux_tv_fill_buffer(demuxer_t *demux)
|
||||
if (want_audio && tvh->tv_param->audio &&
|
||||
tvh->functions->control(tvh->priv,
|
||||
TVI_CONTROL_IS_AUDIO, 0) == TVI_CONTROL_TRUE)
|
||||
{
|
||||
{
|
||||
len = tvh->functions->get_audio_framesize(tvh->priv);
|
||||
|
||||
dp=new_demux_packet(len);
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_audio_frame(tvh->priv, dp->buffer,len);
|
||||
demux_add_packet(want_audio, dp);
|
||||
if (dp) {
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_audio_frame(tvh->priv, dp->buffer,len);
|
||||
demux_add_packet(want_audio, dp);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================== ADD VIDEO PACKET =================== */
|
||||
|
||||
if (want_video && tvh->functions->control(tvh->priv,
|
||||
TVI_CONTROL_IS_VIDEO, 0) == TVI_CONTROL_TRUE)
|
||||
{
|
||||
len = tvh->functions->get_video_framesize(tvh->priv);
|
||||
{
|
||||
len = tvh->functions->get_video_framesize(tvh->priv);
|
||||
dp=new_demux_packet(len);
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_video_frame(tvh->priv, dp->buffer, len);
|
||||
demux_add_packet(want_video, dp);
|
||||
}
|
||||
if (dp) {
|
||||
dp->keyframe = true;
|
||||
dp->pts=tvh->functions->grab_video_frame(tvh->priv, dp->buffer, len);
|
||||
demux_add_packet(want_video, dp);
|
||||
}
|
||||
}
|
||||
|
||||
if (tvh->tv_param->scan) tv_scan(tvh);
|
||||
return 1;
|
||||
|
||||
+7
-4
@@ -34,7 +34,6 @@
|
||||
#include "talloc.h"
|
||||
#include "ebml.h"
|
||||
#include "stream/stream.h"
|
||||
#include "compat/mpbswap.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
// Whether the id is a known Matroska level 1 element (allowed as element on
|
||||
@@ -420,12 +419,16 @@ static void ebml_parse_element(struct ebml_parse_ctx *ctx, void *target,
|
||||
if (num_elems[i] && type->fields[i].multiple) {
|
||||
char *ptr = s + type->fields[i].offset;
|
||||
switch (type->fields[i].desc->type) {
|
||||
case EBML_TYPE_SUBELEMENTS:
|
||||
num_elems[i] = FFMIN(num_elems[i],
|
||||
1000000000 / type->fields[i].desc->size);
|
||||
case EBML_TYPE_SUBELEMENTS: {
|
||||
size_t max = 1000000000 / type->fields[i].desc->size;
|
||||
if (num_elems[i] > max) {
|
||||
MP_ERR(ctx, "Too many subelements.\n");
|
||||
num_elems[i] = max;
|
||||
}
|
||||
int sz = num_elems[i] * type->fields[i].desc->size;
|
||||
*(generic_struct **) ptr = talloc_zero_size(ctx->talloc_ctx, sz);
|
||||
break;
|
||||
}
|
||||
case EBML_TYPE_UINT:
|
||||
*(uint64_t **) ptr = talloc_zero_array(ctx->talloc_ctx,
|
||||
uint64_t, num_elems[i]);
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "stream/stream.h"
|
||||
#include "bstr/bstr.h"
|
||||
#include "misc/bstr.h"
|
||||
|
||||
struct mp_log;
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_MS_HDR_H
|
||||
#define MPLAYER_MS_HDR_H
|
||||
|
||||
#include "compat/mpbswap.h"
|
||||
#include "video/img_fourcc.h"
|
||||
|
||||
// These structs must be binary-compatible to the native win32 types,
|
||||
// because demux_mkv.c uses them directly.
|
||||
|
||||
typedef struct __attribute__((__packed__)) MP_WAVEFORMATEX {
|
||||
unsigned short wFormatTag;
|
||||
unsigned short nChannels;
|
||||
unsigned int nSamplesPerSec;
|
||||
unsigned int nAvgBytesPerSec;
|
||||
unsigned short nBlockAlign;
|
||||
unsigned short wBitsPerSample;
|
||||
unsigned short cbSize;
|
||||
} MP_WAVEFORMATEX;
|
||||
|
||||
typedef struct __attribute__((__packed__)) MP_BITMAPINFOHEADER {
|
||||
int biSize;
|
||||
int biWidth;
|
||||
int biHeight;
|
||||
short biPlanes;
|
||||
short biBitCount;
|
||||
int biCompression;
|
||||
int biSizeImage;
|
||||
int biXPelsPerMeter;
|
||||
int biYPelsPerMeter;
|
||||
int biClrUsed;
|
||||
int biClrImportant;
|
||||
} MP_BITMAPINFOHEADER;
|
||||
|
||||
#endif /* MPLAYER_MS_HDR_H */
|
||||
+43
-50
@@ -29,55 +29,60 @@
|
||||
static void packet_destroy(void *ptr)
|
||||
{
|
||||
struct demux_packet *dp = ptr;
|
||||
talloc_free(dp->avpacket);
|
||||
av_free(dp->allocation);
|
||||
av_packet_unref(dp->avpacket);
|
||||
}
|
||||
|
||||
static struct demux_packet *create_packet(size_t len)
|
||||
// This actually preserves only data and side data, not PTS/DTS/pos/etc.
|
||||
// It also allows avpkt->data==NULL with avpkt->size!=0 - the libavcodec API
|
||||
// does not allow it, but we do it to simplify new_demux_packet().
|
||||
struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt)
|
||||
{
|
||||
if (len > 1000000000) {
|
||||
fprintf(stderr, "Attempt to allocate demux packet over 1 GB!\n");
|
||||
abort();
|
||||
}
|
||||
if (avpkt->size > 1000000000)
|
||||
return NULL;
|
||||
struct demux_packet *dp = talloc(NULL, struct demux_packet);
|
||||
talloc_set_destructor(dp, packet_destroy);
|
||||
*dp = (struct demux_packet) {
|
||||
.len = len,
|
||||
.pts = MP_NOPTS_VALUE,
|
||||
.dts = MP_NOPTS_VALUE,
|
||||
.duration = -1,
|
||||
.pos = -1,
|
||||
.stream = -1,
|
||||
.avpacket = talloc_zero(dp, AVPacket),
|
||||
};
|
||||
av_init_packet(dp->avpacket);
|
||||
int r = -1;
|
||||
if (avpkt->data) {
|
||||
// We hope that this function won't need/access AVPacket input padding,
|
||||
// because otherwise new_demux_packet_from() wouldn't work.
|
||||
r = av_packet_ref(dp->avpacket, avpkt);
|
||||
} else {
|
||||
r = av_new_packet(dp->avpacket, avpkt->size);
|
||||
}
|
||||
if (r < 0) {
|
||||
*dp->avpacket = (AVPacket){0};
|
||||
talloc_free(dp);
|
||||
return NULL;
|
||||
}
|
||||
dp->buffer = dp->avpacket->data;
|
||||
dp->len = dp->avpacket->size;
|
||||
return dp;
|
||||
}
|
||||
|
||||
// Input data doesn't need to be padded.
|
||||
struct demux_packet *new_demux_packet_from(void *data, size_t len)
|
||||
{
|
||||
if (len > INT_MAX)
|
||||
return NULL;
|
||||
AVPacket pkt = { .data = data, .size = len };
|
||||
return new_demux_packet_from_avpacket(&pkt);
|
||||
}
|
||||
|
||||
struct demux_packet *new_demux_packet(size_t len)
|
||||
{
|
||||
struct demux_packet *dp = create_packet(len);
|
||||
dp->buffer = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (!dp->buffer) {
|
||||
fprintf(stderr, "Memory allocation failure!\n");
|
||||
abort();
|
||||
}
|
||||
memset(dp->buffer + len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
dp->allocation = dp->buffer;
|
||||
return dp;
|
||||
}
|
||||
|
||||
// data must already have suitable padding, and does not copy the data
|
||||
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len)
|
||||
{
|
||||
struct demux_packet *dp = create_packet(len);
|
||||
dp->buffer = data;
|
||||
return dp;
|
||||
}
|
||||
|
||||
struct demux_packet *new_demux_packet_from(void *data, size_t len)
|
||||
{
|
||||
struct demux_packet *dp = new_demux_packet(len);
|
||||
memcpy(dp->buffer, data, len);
|
||||
return dp;
|
||||
if (len > INT_MAX)
|
||||
return NULL;
|
||||
AVPacket pkt = { .data = NULL, .size = len };
|
||||
return new_demux_packet_from_avpacket(&pkt);
|
||||
}
|
||||
|
||||
void demux_packet_shorten(struct demux_packet *dp, size_t len)
|
||||
@@ -92,29 +97,17 @@ void free_demux_packet(struct demux_packet *dp)
|
||||
talloc_free(dp);
|
||||
}
|
||||
|
||||
static void destroy_avpacket(void *pkt)
|
||||
{
|
||||
av_free_packet(pkt);
|
||||
}
|
||||
|
||||
struct demux_packet *demux_copy_packet(struct demux_packet *dp)
|
||||
{
|
||||
struct demux_packet *new = NULL;
|
||||
if (dp->avpacket) {
|
||||
assert(dp->buffer == dp->avpacket->data);
|
||||
assert(dp->len == dp->avpacket->size);
|
||||
AVPacket *newavp = talloc_zero(NULL, AVPacket);
|
||||
talloc_set_destructor(newavp, destroy_avpacket);
|
||||
av_init_packet(newavp);
|
||||
if (av_packet_ref(newavp, dp->avpacket) < 0)
|
||||
abort();
|
||||
new = new_demux_packet_fromdata(newavp->data, newavp->size);
|
||||
new->avpacket = newavp;
|
||||
}
|
||||
if (!new) {
|
||||
new = new_demux_packet(dp->len);
|
||||
memcpy(new->buffer, dp->buffer, new->len);
|
||||
new = new_demux_packet_from_avpacket(dp->avpacket);
|
||||
} else {
|
||||
// Some packets might be not created by new_demux_packet*().
|
||||
new = new_demux_packet_from(dp->buffer, dp->len);
|
||||
}
|
||||
if (!new)
|
||||
return NULL;
|
||||
new->pts = dp->pts;
|
||||
new->dts = dp->dts;
|
||||
new->duration = dp->duration;
|
||||
|
||||
+2
-4
@@ -34,13 +34,11 @@ typedef struct demux_packet {
|
||||
bool keyframe;
|
||||
int stream; // source stream index
|
||||
struct demux_packet *next;
|
||||
void *allocation;
|
||||
struct AVPacket *avpacket; // original libavformat packet (demux_lavf)
|
||||
struct AVPacket *avpacket; // keep the buffer allocation
|
||||
} demux_packet_t;
|
||||
|
||||
struct demux_packet *new_demux_packet(size_t len);
|
||||
// data must already have suitable padding
|
||||
struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
|
||||
struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt);
|
||||
struct demux_packet *new_demux_packet_from(void *data, size_t len);
|
||||
void demux_packet_shorten(struct demux_packet *dp, size_t len);
|
||||
void free_demux_packet(struct demux_packet *dp);
|
||||
|
||||
+10
-5
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "common/common.h"
|
||||
#include "audio/chmap.h"
|
||||
#include "ms_hdr.h"
|
||||
|
||||
struct MPOpts;
|
||||
struct demuxer;
|
||||
|
||||
@@ -55,6 +55,7 @@ struct sh_stream {
|
||||
char *title;
|
||||
char *lang; // language code
|
||||
bool default_track; // container default track flag
|
||||
int hls_bitrate;
|
||||
|
||||
// stream is a picture (such as album art)
|
||||
struct demux_packet *attached_picture;
|
||||
@@ -66,10 +67,10 @@ struct sh_stream {
|
||||
typedef struct sh_audio {
|
||||
int samplerate;
|
||||
struct mp_chmap channels;
|
||||
bool force_channels;
|
||||
int bitrate; // compressed bits/sec
|
||||
// win32-compatible codec parameters:
|
||||
MP_WAVEFORMATEX *wf;
|
||||
// note codec extradata may be either under "wf" or "codecdata"
|
||||
int block_align;
|
||||
int bits_per_coded_sample;
|
||||
unsigned char *codecdata;
|
||||
int codecdata_len;
|
||||
struct replaygain_data *replaygain_data;
|
||||
@@ -80,9 +81,13 @@ typedef struct sh_video {
|
||||
float fps; // frames per second (set only if constant fps)
|
||||
float aspect; // aspect ratio stored in the file (for prescaling)
|
||||
int bitrate; // compressed bits/sec
|
||||
int bits_per_coded_sample;
|
||||
int coded_width, coded_height;
|
||||
unsigned char *extradata;
|
||||
int extradata_len;
|
||||
int disp_w, disp_h; // display size
|
||||
int rotate; // intended display rotation, in degrees, [0, 359]
|
||||
MP_BITMAPINFOHEADER *bih;
|
||||
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
|
||||
} sh_video_t;
|
||||
|
||||
typedef struct sh_sub {
|
||||
|
||||
+3
-4
@@ -86,11 +86,10 @@
|
||||
#
|
||||
# Disable the behavior that the player will pause if the cache goes below a
|
||||
# certain fill size.
|
||||
#cache-pause-below=no
|
||||
#cache-pause=no
|
||||
#
|
||||
# Enable demuxer-based cache. This reads ahead a number of packets, and the
|
||||
# player won't freeze completely if network is slow during playback.
|
||||
#demuxer-thread=yes
|
||||
# Read ahead about 5 seconds of audio and video packets.
|
||||
#demuxer-readahead-secs=5.0
|
||||
|
||||
# Display English subtitles if available.
|
||||
#slang=en
|
||||
|
||||
+14
-15
@@ -33,8 +33,8 @@
|
||||
#MOUSE_BTN2 cycle pause # toggle pause on/off
|
||||
#MOUSE_BTN3 seek 10
|
||||
#MOUSE_BTN4 seek -10
|
||||
#MOUSE_BTN5 add volume 1
|
||||
#MOUSE_BTN6 add volume -1
|
||||
#MOUSE_BTN5 add volume 2
|
||||
#MOUSE_BTN6 add volume -2
|
||||
|
||||
# Mouse wheels, touchpad or other input devices that have axes
|
||||
# if the input devices supports precise scrolling it will also scale the
|
||||
@@ -67,7 +67,7 @@
|
||||
#q quit
|
||||
#Q quit_watch_later
|
||||
#q {encode} quit
|
||||
#ESC quit
|
||||
#ESC set fullscreen no
|
||||
#ESC {encode} quit
|
||||
#p cycle pause # toggle pause/playback mode
|
||||
#. frame_step # advance one frame and pause
|
||||
@@ -84,10 +84,10 @@
|
||||
#x add sub-delay +0.1 # add
|
||||
#ctrl++ add audio-delay 0.100 # this changes audio/video sync
|
||||
#ctrl+- add audio-delay -0.100
|
||||
#9 add volume -1
|
||||
#/ add volume -1
|
||||
#0 add volume 1
|
||||
#* add volume 1
|
||||
#9 add volume -2
|
||||
#/ add volume -2
|
||||
#0 add volume 2
|
||||
#* add volume 2
|
||||
#m cycle mute
|
||||
#1 add contrast -1
|
||||
#2 add contrast 1
|
||||
@@ -126,12 +126,13 @@
|
||||
#REWIND seek -60
|
||||
#NEXT playlist_next
|
||||
#PREV playlist_prev
|
||||
#VOLUME_UP add volume 1
|
||||
#VOLUME_DOWN add volume -1
|
||||
#VOLUME_UP add volume 2
|
||||
#VOLUME_DOWN add volume -2
|
||||
#MUTE cycle mute
|
||||
#CLOSE_WIN quit
|
||||
#CLOSE_WIN {encode} quit
|
||||
#E cycle edition # next edition
|
||||
#ctrl+c quit
|
||||
|
||||
# Apple Remote section
|
||||
#AR_PLAY cycle pause
|
||||
@@ -144,9 +145,9 @@
|
||||
#AR_PREV_HOLD seek -120
|
||||
#AR_MENU show_progress
|
||||
#AR_MENU_HOLD cycle mute
|
||||
#AR_VUP add volume 1
|
||||
#AR_VUP add volume 2
|
||||
#AR_VUP_HOLD add chapter 1
|
||||
#AR_VDOWN add volume -1
|
||||
#AR_VDOWN add volume -2
|
||||
#AR_VDOWN_HOLD add chapter -1
|
||||
|
||||
# Joystick section
|
||||
@@ -160,8 +161,8 @@
|
||||
#JOY_AXIS1_PLUS seek -60
|
||||
#JOY_BTN0 cycle pause
|
||||
#JOY_BTN1 cycle osd
|
||||
#JOY_BTN2 add volume 1
|
||||
#JOY_BTN3 add volume -1
|
||||
#JOY_BTN2 add volume 2
|
||||
#JOY_BTN3 add volume -2
|
||||
|
||||
# For dvdnav:// and bdnav://
|
||||
|
||||
@@ -206,5 +207,3 @@
|
||||
# ? cycle sub-forced-only # toggle DVD forced subs
|
||||
# ? cycle program # cycle transport stream programs
|
||||
# ? stop # stop playback (quit or enter idle mode)
|
||||
# ? cycle quvi-format 1 # cycle streaming video quality (semi-broken)
|
||||
# ? cycle quvi-format -1
|
||||
@@ -13,7 +13,7 @@ UP seek +60
|
||||
PGUP seek 600
|
||||
PGDWN seek -600
|
||||
m cycle mute
|
||||
# cycle audio # switch audio streams
|
||||
SHARP cycle audio # switch audio streams
|
||||
+ add audio-delay 0.100
|
||||
= add audio-delay 0.100
|
||||
- add audio-delay -0.100
|
||||
@@ -28,7 +28,7 @@ ENTER playlist_next force # skip to next file
|
||||
p cycle pause
|
||||
. frame_step # advance one frame and pause
|
||||
SPACE cycle pause
|
||||
HOME set playlist-pos 1 # not the same as MPlayer
|
||||
HOME set playlist-pos 0 # not the same as MPlayer
|
||||
#END pt_up_step -1
|
||||
> playlist_next # skip to next file
|
||||
< playlist_prev # previous
|
||||
|
||||
Loaded 100 of 266 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user