-
Notifications
You must be signed in to change notification settings - Fork 2
/
playermain.cpp
152 lines (143 loc) · 4.53 KB
/
playermain.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <cstdio>
#include <cstring>
#include <filesystem>
#include "cuesheet.h"
#include "effect.h"
#include "eventcallbacks.h"
#include "ffmpeg.h"
#include "mpris2.h"
#include "notifier_impl.h"
#include "notify.h"
#include "player.h"
#include "pulse.h"
#include "resource.h"
#include "threadpool.h"
namespace fs = std::filesystem;
using fs::recursive_directory_iterator;
using dp = std::filesystem::directory_options;
struct resource {
std::string url;
std::string title;
unsigned long seekto;
};
struct playlist {
std::vector<resource> list;
};
struct file_handler {
char extension[8];
void (*file_handler_func)(playlist *pl, const char *filename);
};
struct playercontext {
vrok::Player player;
vrok::DriverPulse out;
vrok::ThreadPool pool;
bool ispaused;
playercontext() : pool(2), ispaused(false) { }
};
void randomplay(playercontext *ctx, playlist *pl);
class playerevents : public vrok::Player::Events {
public:
playerevents(playercontext *ctx, playlist *pl) : _ctx(ctx), _pl(pl) { }
void QueueNext() { randomplay(_ctx, _pl); }
private:
playlist *_pl;
playercontext *_ctx;
};
void enque(playlist *pl, const char *filename) {
const auto &p = fs::path(filename);
pl->list.push_back(resource{ filename, p.filename(), 0 });
}
unsigned long time_to_secs(int hours, int mins, int secs) {
return (hours * 3600) + (mins * 60) + secs;
}
void enque_cuesheet(playlist *pl, const char *filename) {
cuesh_data *cd = cuesh_init(filename);
if (cd == nullptr) {
return;
}
const auto &p = fs::path(filename);
auto parent = p.parent_path();
parent += "/";
parent += cd->filename;
for (auto &track : cd->tracks) {
printf("%d %d\n", track->minutes, track->seconds);
pl->list.push_back(resource{ .url = parent,
.title = track->title,
.seekto = time_to_secs(track->hours, track->minutes, track->seconds) });
}
}
file_handler handlers[] = { { "mp3", enque }, { "webm", enque }, { "mp4", enque }, { "m4a", enque },
{ "aac", enque }, { "flac", enque }, { "cue", enque_cuesheet } };
playlist mainpl;
void enum_files(const char *dirname) {
for (const auto &file : recursive_directory_iterator(dirname, dp::skip_permission_denied)) {
std::string pathstr = file.path();
std::string extstr = file.path().extension();
if (extstr.size() > 1) {
extstr = extstr.substr(1);
for (const auto &handler : handlers) {
if (strcmp(handler.extension, extstr.c_str()) == 0) {
handler.file_handler_func(&mainpl, pathstr.c_str());
}
}
}
}
}
void list_pl() {
for (auto &r : mainpl.list) {
printf("%s %f\n", r.url.c_str(), r.seekto);
}
}
playercontext *plctx_create() {
vrok::Notify::GetInstance()->SetNotifier(new CNotifier);
playercontext *ctx = new playercontext();
ctx->player.SetQueueNext(true);
ctx->player.SetEvents(new playerevents(ctx, &mainpl));
ctx->player.Preallocate();
ctx->out.Preallocate();
ctx->out.RegisterSource(&ctx->player);
ctx->player.RegisterSink(&ctx->out);
ctx->pool.RegisterWork(0, &ctx->player);
ctx->pool.RegisterWork(1, &ctx->out);
return ctx;
}
void randomplay(playercontext *ctx, playlist *pl) {
vrok::DecoderFFMPEG *dec = new vrok::DecoderFFMPEG();
int index = rand() % pl->list.size();
vrok::Resource *res = new vrok::Resource{ pl->list[index].url };
printf("Playing: %s %lu\n", pl->list[index].title.c_str(), pl->list[index].seekto);
dec->Open(res);
dec->SetPositionInSeconds(pl->list[index].seekto);
ctx->player.SubmitForPlayback(dec);
}
void on_event_playpause(void *user) {
playercontext *ctx = (playercontext *)user;
if (ctx->ispaused) {
ctx->player.Resume();
ctx->ispaused = false;
} else {
ctx->player.Pause();
ctx->ispaused = true;
}
}
void on_event_next(void *user) {
playercontext *ctx = (playercontext *)user;
ctx->player.Stop();
ctx->player.Flush();
randomplay(ctx, &mainpl);
}
int main(int argc, const char **argv) {
srand(time(NULL));
event_callbacks events{ .on_playpause = on_event_playpause, .on_next = on_event_next };
const auto &ctx = plctx_create();
mpris2_init(&events, ctx);
ctx->pool.CreateThreads();
enum_files(argv[1]);
list_pl();
randomplay(ctx, &mainpl);
while (1) {
mpris2_process_events();
}
ctx->pool.JoinThreads();
return 0;
}