342 lines
7.6 KiB
C
342 lines
7.6 KiB
C
#include <Mw/Milsko.h>
|
|
#include <Mw/Widget/OpenGL.h>
|
|
|
|
#include "pl_mpeg.h"
|
|
#include "miniaudio.h"
|
|
#include "stb_ds.h"
|
|
|
|
#define MULTIPLE_BUFFER
|
|
#define PERIOD PLM_AUDIO_SAMPLES_PER_FRAME
|
|
#define BUFFERS 3
|
|
|
|
typedef struct buffer {
|
|
float buffer[PERIOD * 2];
|
|
} buffer_t;
|
|
|
|
static ma_device device;
|
|
static buffer_t buffers[BUFFERS];
|
|
static int buf_rseek = 0;
|
|
static int buf_wseek = 0;
|
|
static int buf_rframe = 0;
|
|
static int buf_wframe = 0;
|
|
static ma_mutex buf_mutex;
|
|
static MwWidget box, opengl, progress;
|
|
static plm_t* plm = NULL;
|
|
static GLuint tex = 0;
|
|
static unsigned char* vid = NULL;
|
|
static int vw, vh;
|
|
static int ww, wh;
|
|
static double last;
|
|
static int progress_move = 1;
|
|
static int paused = 0;
|
|
static int sample_rate = 0;
|
|
|
|
static void camera(int w, int h){
|
|
MwOpenGLMakeCurrent(opengl);
|
|
|
|
glViewport(0, 0, w, h);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, w, h, 0, -1, 1);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
}
|
|
|
|
static void MWAPI resize(MwWidget widget, void* user, void* call){
|
|
ww = MwGetInteger(widget, MwNwidth);
|
|
wh = MwGetInteger(widget, MwNheight);
|
|
|
|
MwVaApply(box,
|
|
MwNwidth, ww,
|
|
MwNheight, wh,
|
|
NULL);
|
|
}
|
|
|
|
static void MWAPI opengl_resize(MwWidget widget, void* user, void* call){
|
|
camera(MwGetInteger(widget, MwNwidth), MwGetInteger(widget, MwNheight));
|
|
}
|
|
|
|
static void MWAPI progress_mouseDown(MwWidget widget, void* user, void* call){
|
|
MwMouse* m = call;
|
|
|
|
if(m->button == MwMOUSE_LEFT) progress_move = 0;
|
|
}
|
|
|
|
static void MWAPI progress_mouseUp(MwWidget widget, void* user, void* call){
|
|
MwMouse* m = call;
|
|
|
|
if(m->button == MwMOUSE_LEFT) progress_move = 1;
|
|
|
|
plm_seek(plm, (double)MwGetInteger(widget, MwNvalue) / 1000, 1);
|
|
}
|
|
|
|
static void MWAPI play_activate(MwWidget widget, void* user, void* call){
|
|
plm_rewind(plm);
|
|
}
|
|
|
|
static void MWAPI pause_activate(MwWidget widget, void* user, void* call){
|
|
paused = !paused;
|
|
}
|
|
|
|
static void on_audio(plm_t *mpeg, plm_samples_t *samples, void *user) {
|
|
buffer_t buf;
|
|
int i;
|
|
int total;
|
|
|
|
total = samples->count;
|
|
|
|
for(i = 0; i < total; i++){
|
|
int b = i * 2;
|
|
|
|
buf.buffer[i * 2 + 0] = samples->interleaved[b + 0];
|
|
buf.buffer[i * 2 + 1] = samples->interleaved[b + 1];
|
|
}
|
|
|
|
ma_mutex_lock(&buf_mutex);
|
|
buffers[buf_wseek++] = buf;
|
|
if(buf_wseek == BUFFERS) buf_wseek = 0;
|
|
buf_wframe++;
|
|
ma_mutex_unlock(&buf_mutex);
|
|
}
|
|
|
|
static void on_video(plm_t* mpeg, plm_frame_t* frame, void* user){
|
|
plm_frame_to_rgb(frame, vid, vw * 3);
|
|
|
|
MwOpenGLMakeCurrent(opengl);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vw, vh, GL_RGB, GL_UNSIGNED_BYTE, vid);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
static void MWAPI tick(MwWidget widget, void* user, void* call){
|
|
double sx = 0;
|
|
double sy = 0;
|
|
double sw = 0;
|
|
double sh = 0;
|
|
|
|
if(((double)ww / vw) < ((double)wh / vh)){
|
|
sw = ww;
|
|
sh = (double)ww / vw * vh;
|
|
}else{
|
|
sh = wh;
|
|
sw = (double)wh / vh * vw;
|
|
}
|
|
|
|
sx = ((double)MwGetInteger(opengl, MwNwidth) - sw) / 2;
|
|
sy = ((double)MwGetInteger(opengl, MwNheight) - sh) / 2;
|
|
|
|
MwOpenGLMakeCurrent(opengl);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
|
|
glColor3f(1, 1, 1);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0, 0); glVertex2f(sx, sy);
|
|
glTexCoord2f(0, 1); glVertex2f(sx, sy + sh);
|
|
glTexCoord2f(1, 1); glVertex2f(sx + sw, sy + sh);
|
|
glTexCoord2f(1, 0); glVertex2f(sx + sw, sy);
|
|
glEnd();
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
MwOpenGLSwapBuffer(opengl);
|
|
}
|
|
|
|
static int load(const char* input){
|
|
int i;
|
|
|
|
buf_wframe = buf_wseek = 0;
|
|
|
|
memset(&buffers, 0, sizeof(buffers));
|
|
|
|
if(plm != NULL) plm_destroy(plm);
|
|
|
|
if((plm = plm_create_with_filename(input)) == NULL || !plm_probe(plm, 5000 * 1024)){
|
|
if(plm != NULL){
|
|
plm_destroy(plm);
|
|
plm = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
plm_set_audio_decode_callback(plm, on_audio, NULL);
|
|
plm_set_video_decode_callback(plm, on_video, NULL);
|
|
plm_set_loop(plm, 1);
|
|
|
|
sample_rate = plm_get_samplerate(plm);
|
|
|
|
MwOpenGLMakeCurrent(opengl);
|
|
|
|
vw = plm_get_width(plm);
|
|
vh = plm_get_height(plm);
|
|
|
|
MwVaApply(progress,
|
|
MwNmaxValue, (int)(plm_get_duration(plm) * 1000),
|
|
MwNvalue, 0,
|
|
MwNareaShown, 1000 * 10,
|
|
NULL);
|
|
|
|
if(vid != NULL) free(vid);
|
|
|
|
vid = malloc(vw * vh * 3);
|
|
memset(vid, 0, vw * vh * 3);
|
|
|
|
for(i = 0; i < vw * vh; i++){
|
|
vid[i * 3 + 0] = i / vw * 255 / vh;
|
|
vid[i * 3 + 2] = (i % vw) * 255 / vw;
|
|
}
|
|
|
|
if(tex != 0) glDeleteTextures(1, &tex);
|
|
|
|
glGenTextures(1, &tex);
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, vw, vh, 0, GL_RGB, GL_UNSIGNED_BYTE, vid);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
last = (double)MwTimeGetTick() / 1000;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void data_callback(ma_device* device, void* output, const void* input, ma_uint32 frame){
|
|
short* o = output;
|
|
int i;
|
|
|
|
memset(o, 0, frame * sizeof(short) * 2);
|
|
|
|
ma_mutex_lock(&buf_mutex);
|
|
for(i = 0; i < frame * 2; i++){
|
|
o[i] = buffers[buf_rseek].buffer[i] * 32767;
|
|
}
|
|
ma_mutex_unlock(&buf_mutex);
|
|
|
|
buf_rseek++;
|
|
if(buf_rseek == BUFFERS) buf_rseek = 0;
|
|
buf_rframe++;
|
|
|
|
if(buf_rframe > buf_wframe){
|
|
buf_rframe = buf_wframe;
|
|
buf_rseek = buf_wseek;
|
|
}
|
|
}
|
|
|
|
static int init(void){
|
|
ma_device_config config;
|
|
|
|
ma_mutex_init(&buf_mutex);
|
|
|
|
config = ma_device_config_init(ma_device_type_playback);
|
|
config.playback.format = ma_format_s16;
|
|
config.playback.channels = 2;
|
|
config.sampleRate = 48000;
|
|
config.dataCallback = data_callback;
|
|
config.pUserData = NULL;
|
|
config.periodSizeInFrames = PERIOD;
|
|
|
|
if(ma_device_init(NULL, &config, &device) != MA_SUCCESS){
|
|
return 1;
|
|
}
|
|
|
|
if(ma_device_start(&device) != MA_SUCCESS){
|
|
ma_device_uninit(&device);
|
|
|
|
return 1;
|
|
}
|
|
|
|
MwOpenGLMakeCurrent(opengl);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char** argv){
|
|
MwWidget w, bar, play, pause;
|
|
int st;
|
|
|
|
MwLibraryInit();
|
|
|
|
w = MwVaCreateWidget(MwWindowClass, "main", NULL, MwDEFAULT, MwDEFAULT, 800, 630,
|
|
MwNtitle, "poc player",
|
|
NULL);
|
|
|
|
box = MwVaCreateWidget(MwBoxClass, "box", w, 0, 0, 0, 0,
|
|
MwNorientation, MwVERTICAL,
|
|
NULL);
|
|
|
|
opengl = MwCreateWidget(MwOpenGLClass, "opengl", box, 0, 0, 0, 0);
|
|
|
|
bar = MwVaCreateWidget(MwBoxClass, "bar", box, 0, 0, 0, 0,
|
|
MwNfixedSize, 30,
|
|
MwNorientation, MwHORIZONTAL,
|
|
NULL);
|
|
|
|
play = MwVaCreateWidget(MwButtonClass, "play", bar, 0, 0, 0, 0,
|
|
MwNtext, "Play",
|
|
MwNfixedSize, 60,
|
|
NULL);
|
|
|
|
pause = MwVaCreateWidget(MwButtonClass, "pause", bar, 0, 0, 0, 0,
|
|
MwNtext, "Pause",
|
|
MwNfixedSize, 60,
|
|
NULL);
|
|
|
|
progress = MwVaCreateWidget(MwScrollBarClass, "progress", bar, 0, 0, 0, 0,
|
|
MwNshowArrows, 0,
|
|
MwNorientation, MwHORIZONTAL,
|
|
NULL);
|
|
|
|
MwAddUserHandler(w, MwNresizeHandler, resize, NULL);
|
|
MwAddUserHandler(w, MwNtickHandler, tick, NULL);
|
|
MwAddUserHandler(opengl, MwNresizeHandler, opengl_resize, NULL);
|
|
MwAddUserHandler(progress, MwNmouseDownHandler, progress_mouseDown, NULL);
|
|
MwAddUserHandler(progress, MwNmouseUpHandler, progress_mouseUp, NULL);
|
|
MwAddUserHandler(play, MwNactivateHandler, play_activate, NULL);
|
|
MwAddUserHandler(pause, MwNactivateHandler, pause_activate, NULL);
|
|
|
|
if((st = init()) != 0) return st;
|
|
|
|
resize(w, NULL, NULL);
|
|
|
|
if((st = load(argv[1])) != 0) return st;
|
|
|
|
while(1){
|
|
double l2, elapsed;
|
|
|
|
while(MwPending(w)) MwStep(w);
|
|
|
|
elapsed = (l2 = (double)MwTimeGetTick() / 1000) - last;
|
|
|
|
if(elapsed > 1.0 / 30) elapsed = 1.0 / 30;
|
|
|
|
if(paused){
|
|
buffer_t buf;
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
|
|
ma_mutex_lock(&buf_mutex);
|
|
buffers[buf_wseek] = buf;
|
|
if(buf_wseek == BUFFERS) buf_wseek = 0;
|
|
ma_mutex_unlock(&buf_mutex);
|
|
}else{
|
|
plm_decode(plm, elapsed);
|
|
last = l2;
|
|
|
|
if(progress_move){
|
|
MwVaApply(progress,
|
|
MwNvalue, (int)(plm_get_time(plm) * 1000),
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|