mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Implement DrawMovie, DrawMovie2 and PlayMovie HLLs
These HLLs play .mpg movie files (later games use .alm file extension, but the format is the same, i.e. MPEG-PS) that contain MPEG1 video and MP2 audio streams. pl_mpeg.h is the PL_MPEG library (https://github.com/phoboslab/pl_mpeg) imported from commit 5aeef4d07593be1960a0e4c7fe204809cfae30bd. This adds a few mixer functions (mixer_stream_*) for playing custom audio streams under the master mixer.
This commit is contained in:
@@ -49,6 +49,11 @@ int mixer_set_volume(int n, int volume);
|
||||
int mixer_get_mute(int n, int *mute);
|
||||
int mixer_set_mute(int n, int mute);
|
||||
|
||||
struct sts_mixer_stream_t;
|
||||
int mixer_stream_play(struct sts_mixer_stream_t* stream, int volume);
|
||||
bool mixer_stream_set_volume(int voice, int volume);
|
||||
void mixer_stream_stop(int voice);
|
||||
|
||||
struct archive_data;
|
||||
struct channel;
|
||||
enum asset_type;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_MOVIE_H
|
||||
#define SYSTEM4_MOVIE_H
|
||||
|
||||
struct movie_context;
|
||||
struct sact_sprite;
|
||||
|
||||
struct movie_context *movie_load(const char *filename);
|
||||
void movie_free(struct movie_context *mc);
|
||||
bool movie_play(struct movie_context *mc);
|
||||
bool movie_draw(struct movie_context *mc, struct sact_sprite *sprite);
|
||||
bool movie_is_end(struct movie_context *mc);
|
||||
int movie_get_position(struct movie_context *mc);
|
||||
bool movie_set_volume(struct movie_context *mc, int volume /* 0-100 */);
|
||||
|
||||
#endif /* SYSTEM4_MOVIE_H */
|
||||
+4265
File diff suppressed because it is too large
Load Diff
+9
-9
@@ -39,6 +39,11 @@ enum {
|
||||
STS_MIXER_SAMPLE_FORMAT_FLOAT // floats
|
||||
};
|
||||
|
||||
enum {
|
||||
STS_STREAM_COMPLETE = 0,
|
||||
STS_STREAM_CONTINUE = 1
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -66,7 +71,7 @@ typedef struct {
|
||||
// The callback which will be called when the stream needs more data.
|
||||
typedef int (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
|
||||
|
||||
typedef struct {
|
||||
typedef struct sts_mixer_stream_t {
|
||||
void* userdata; // a userdata pointer which will passed to the callback
|
||||
sts_mixer_stream_callback callback; // this callback will be called when the stream needs more data
|
||||
sts_mixer_sample_t sample; // the current stream "sample" which holds the current piece of audio
|
||||
@@ -160,11 +165,6 @@ enum {
|
||||
STS_MIXER_VOICE_STREAMING
|
||||
};
|
||||
|
||||
enum {
|
||||
STS_STREAM_COMPLETE = 0,
|
||||
STS_STREAM_CONTINUE = 1
|
||||
};
|
||||
|
||||
static float sts_mixer__clamp(const float value, const float min, const float max) {
|
||||
if (value < min) return min;
|
||||
else if (value > max) return max;
|
||||
@@ -329,14 +329,14 @@ void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples)
|
||||
voice->position = 0.0f;
|
||||
position = 0;
|
||||
}
|
||||
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
|
||||
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
|
||||
// added in xsystem4: allow stopping stream via callback return value
|
||||
if (status == STS_STREAM_COMPLETE) {
|
||||
sts_mixer_stop_voice(mixer, i);
|
||||
} else {
|
||||
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
|
||||
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
|
||||
voice->position += (float)voice->stream->sample.frequency * advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D texture_y;
|
||||
uniform sampler2D texture_cb;
|
||||
uniform sampler2D texture_cr;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
mat4 rec601 = mat4(
|
||||
1.16438, 0.00000, 1.59603, -0.87079,
|
||||
1.16438, -0.39176, -0.81297, 0.52959,
|
||||
1.16438, 2.01723, 0.00000, -1.08139,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
);
|
||||
|
||||
void main() {
|
||||
float y = texture(texture_y, tex_coord).r;
|
||||
float cb = texture(texture_cb, tex_coord).r;
|
||||
float cr = texture(texture_cr, tex_coord).r;
|
||||
|
||||
frag_color = vec4(y, cb, cr, 1.0) * rec601;
|
||||
}
|
||||
@@ -717,3 +717,29 @@ int mixer_set_mute(int n, int mute)
|
||||
mixers[n].muted = !!mute;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mixer_stream_play(sts_mixer_stream_t* stream, int volume)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
float gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
int voice = sts_mixer_play_stream(&master->mixer, stream, gain);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return voice;
|
||||
}
|
||||
|
||||
bool mixer_stream_set_volume(int voice, int volume)
|
||||
{
|
||||
if (voice < 0 || voice >= STS_MIXER_VOICES)
|
||||
return false;
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
master->mixer.voices[voice].gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mixer_stream_stop(int voice)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
sts_mixer_stop_voice(&master->mixer, voice);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
}
|
||||
|
||||
+76
-13
@@ -14,22 +14,85 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawMovie, Release);
|
||||
HLL_WARN_UNIMPLEMENTED(0, bool, DrawMovie, Load, possibly_unused struct string *filename);
|
||||
//bool DrawMovie_Run(void);
|
||||
//bool DrawMovie_Draw(int nSprite);
|
||||
//bool DrawMovie_SetVolume(int nVolume);
|
||||
//bool DrawMovie_IsEnd(void);
|
||||
//int DrawMovie_GetCount(void);
|
||||
#include "hll.h"
|
||||
#include "movie.h"
|
||||
#include "sact.h"
|
||||
|
||||
static struct movie_context *mc;
|
||||
|
||||
static void DrawMovie_Release(void)
|
||||
{
|
||||
if (mc)
|
||||
movie_free(mc);
|
||||
mc = NULL;
|
||||
}
|
||||
|
||||
static bool DrawMovie_Load(struct string *filename)
|
||||
{
|
||||
if (mc)
|
||||
movie_free(mc);
|
||||
mc = movie_load(filename->text);
|
||||
return !!mc;
|
||||
}
|
||||
|
||||
static bool DrawMovie_Run(void)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return movie_play(mc);
|
||||
}
|
||||
|
||||
static bool DrawMovie_Draw(int sprite)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
|
||||
return movie_draw(mc, sp);
|
||||
}
|
||||
|
||||
static bool DrawMovie_SetVolume(int volume)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return movie_set_volume(mc, volume);
|
||||
}
|
||||
|
||||
static bool DrawMovie_IsEnd(void)
|
||||
{
|
||||
if (!mc)
|
||||
return true;
|
||||
return movie_is_end(mc);
|
||||
}
|
||||
|
||||
static int DrawMovie_GetCount(void)
|
||||
{
|
||||
if (!mc)
|
||||
return 0;
|
||||
return movie_get_position(mc);
|
||||
}
|
||||
|
||||
HLL_LIBRARY(DrawMovie,
|
||||
HLL_EXPORT(Release, DrawMovie_Release),
|
||||
HLL_EXPORT(Load, DrawMovie_Load),
|
||||
HLL_TODO_EXPORT(Run, DrawMovie_Run),
|
||||
HLL_TODO_EXPORT(Draw, DrawMovie_Draw),
|
||||
HLL_TODO_EXPORT(SetVolume, DrawMovie_SetVolume),
|
||||
HLL_TODO_EXPORT(IsEnd, DrawMovie_IsEnd),
|
||||
HLL_TODO_EXPORT(GetCount, DrawMovie_GetCount)
|
||||
HLL_EXPORT(Run, DrawMovie_Run),
|
||||
HLL_EXPORT(Draw, DrawMovie_Draw),
|
||||
HLL_EXPORT(SetVolume, DrawMovie_SetVolume),
|
||||
HLL_EXPORT(IsEnd, DrawMovie_IsEnd),
|
||||
HLL_EXPORT(GetCount, DrawMovie_GetCount)
|
||||
);
|
||||
|
||||
HLL_LIBRARY(DrawMovie2,
|
||||
HLL_EXPORT(Release, DrawMovie_Release),
|
||||
HLL_EXPORT(Load, DrawMovie_Load),
|
||||
HLL_EXPORT(Run, DrawMovie_Run),
|
||||
HLL_EXPORT(Draw, DrawMovie_Draw),
|
||||
HLL_EXPORT(SetVolume, DrawMovie_SetVolume),
|
||||
HLL_EXPORT(IsEnd, DrawMovie_IsEnd),
|
||||
HLL_EXPORT(GetCount, DrawMovie_GetCount)
|
||||
);
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawMovie2, Release);
|
||||
HLL_WARN_UNIMPLEMENTED(0, bool, DrawMovie2, Load, possibly_unused struct string *filename);
|
||||
//bool DrawMovie2_Run(void);
|
||||
//bool DrawMovie2_Draw(int nSprite);
|
||||
//bool DrawMovie2_SetVolume(int nVolume);
|
||||
//bool DrawMovie2_IsEnd(void);
|
||||
//int DrawMovie2_GetCount(void);
|
||||
|
||||
HLL_LIBRARY(DrawMovie2,
|
||||
HLL_EXPORT(Release, DrawMovie2_Release),
|
||||
HLL_EXPORT(Load, DrawMovie2_Load),
|
||||
HLL_TODO_EXPORT(Run, DrawMovie2_Run),
|
||||
HLL_TODO_EXPORT(Draw, DrawMovie2_Draw),
|
||||
HLL_TODO_EXPORT(SetVolume, DrawMovie2_SetVolume),
|
||||
HLL_TODO_EXPORT(IsEnd, DrawMovie2_IsEnd),
|
||||
HLL_TODO_EXPORT(GetCount, DrawMovie2_GetCount)
|
||||
);
|
||||
+60
-13
@@ -14,20 +14,67 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, PlayMovie, Init, void);
|
||||
//int PlayMovie_Load(struct string *filename);
|
||||
//int PlayMovie_Play(void);
|
||||
//void PlayMovie_Stop(void);
|
||||
//int PlayMovie_IsPlay(void);
|
||||
//void PlayMovie_Release(void);
|
||||
#include "hll.h"
|
||||
#include "movie.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
static struct movie_context *mc;
|
||||
|
||||
static int PlayMovie_Init(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int PlayMovie_Load(struct string *filename)
|
||||
{
|
||||
if (mc)
|
||||
movie_free(mc);
|
||||
mc = movie_load(filename->text);
|
||||
return !!mc;
|
||||
}
|
||||
|
||||
static int PlayMovie_Play(void)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return movie_play(mc);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(, void, PlayMovie, Stop, void);
|
||||
|
||||
// PlayMovie is used like this:
|
||||
//
|
||||
// PlayMovie.Load(filename);
|
||||
// PlayMovie.Play();
|
||||
// while (PlayMovie.IsPlay()) {
|
||||
// system.Peek();
|
||||
// system.Sleep(1);
|
||||
// }
|
||||
//
|
||||
// SACT.Update is not called inside the loop, so we do rendering in IsPlay().
|
||||
static int PlayMovie_IsPlay(void)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
if (!movie_draw(mc, NULL))
|
||||
return false;
|
||||
return !movie_is_end(mc);
|
||||
}
|
||||
|
||||
static void PlayMovie_Release(void)
|
||||
{
|
||||
if (mc)
|
||||
movie_free(mc);
|
||||
mc = NULL;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(PlayMovie,
|
||||
HLL_EXPORT(Init, PlayMovie_Init)
|
||||
//HLL_EXPORT(Load, PlayMovie_Load),
|
||||
//HLL_EXPORT(Play, PlayMovie_Play),
|
||||
//HLL_EXPORT(Stop, PlayMovie_Stop),
|
||||
//HLL_EXPORT(IsPlay, PlayMovie_IsPlay),
|
||||
//HLL_EXPORT(Release, PlayMovie_Release)
|
||||
HLL_EXPORT(Init, PlayMovie_Init),
|
||||
HLL_EXPORT(Load, PlayMovie_Load),
|
||||
HLL_EXPORT(Play, PlayMovie_Play),
|
||||
HLL_EXPORT(Stop, PlayMovie_Stop),
|
||||
HLL_EXPORT(IsPlay, PlayMovie_IsPlay),
|
||||
HLL_EXPORT(Release, PlayMovie_Release)
|
||||
);
|
||||
|
||||
+1
-1
@@ -20,6 +20,7 @@ xsystem4 = [version_h,
|
||||
'heap.c',
|
||||
'id_pool.c',
|
||||
'input.c',
|
||||
'movie.c',
|
||||
'page.c',
|
||||
'resume.c',
|
||||
'savedata.c',
|
||||
@@ -80,7 +81,6 @@ xsystem4 = [version_h,
|
||||
'hll/DrawDungeon.c',
|
||||
'hll/DrawGraph.c',
|
||||
'hll/DrawMovie.c',
|
||||
'hll/DrawMovie2.c',
|
||||
'hll/DrawPluginManager.c',
|
||||
'hll/DrawSimpleText.c',
|
||||
'hll/File.c',
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "movie.h"
|
||||
#include "mixer.h"
|
||||
#include "sprite.h"
|
||||
#include "sts_mixer.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define PL_MPEG_IMPLEMENTATION
|
||||
#include "pl_mpeg.h"
|
||||
|
||||
static Shader movie_shader;
|
||||
|
||||
struct movie_context {
|
||||
plm_t *plm;
|
||||
SDL_mutex *decoder_mutex;
|
||||
|
||||
plm_frame_t *pending_video_frame;
|
||||
GLuint textures[3]; // Y, Cb, Cr
|
||||
|
||||
sts_mixer_stream_t sts_stream;
|
||||
int voice;
|
||||
int volume;
|
||||
|
||||
// Time keeping data. Written by audio handler and referenced by video
|
||||
// handler (i.e. we sync the video to the audio).
|
||||
double stream_time; // in seconds
|
||||
uint32_t wall_time_ms;
|
||||
SDL_mutex *timer_mutex;
|
||||
};
|
||||
|
||||
static int audio_callback(sts_mixer_sample_t *sample, void *data)
|
||||
{
|
||||
struct movie_context *mc = data;
|
||||
assert(sample == &mc->sts_stream.sample);
|
||||
|
||||
SDL_LockMutex(mc->decoder_mutex);
|
||||
plm_samples_t *frame = plm_decode_audio(mc->plm);
|
||||
SDL_UnlockMutex(mc->decoder_mutex);
|
||||
if (!frame) {
|
||||
sample->length = 0;
|
||||
sample->data = NULL;
|
||||
mc->voice = -1;
|
||||
return STS_STREAM_COMPLETE;
|
||||
}
|
||||
sample->length = frame->count * 2;
|
||||
sample->data = frame->interleaved;
|
||||
|
||||
// Update the timestamp.
|
||||
SDL_LockMutex(mc->timer_mutex);
|
||||
mc->stream_time = frame->time;
|
||||
mc->wall_time_ms = SDL_GetTicks();
|
||||
SDL_UnlockMutex(mc->timer_mutex);
|
||||
return STS_STREAM_CONTINUE;
|
||||
}
|
||||
|
||||
static void prepare_movie_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct movie_context *mc = data;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, mc->textures[0]);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, mc->textures[1]);
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, mc->textures[2]);
|
||||
}
|
||||
|
||||
static void load_movie_shader()
|
||||
{
|
||||
gfx_load_shader(&movie_shader, "shaders/render.v.glsl", "shaders/movie.f.glsl");
|
||||
glUseProgram(movie_shader.program);
|
||||
glUniform1i(glGetUniformLocation(movie_shader.program, "texture_y"), 0);
|
||||
glUniform1i(glGetUniformLocation(movie_shader.program, "texture_cb"), 1);
|
||||
glUniform1i(glGetUniformLocation(movie_shader.program, "texture_cr"), 2);
|
||||
movie_shader.prepare = prepare_movie_shader;
|
||||
}
|
||||
|
||||
static void update_texture(GLuint unit, GLuint texture, plm_plane_t *plane)
|
||||
{
|
||||
glActiveTexture(unit);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, plane->width, plane->height, 0,
|
||||
GL_RED, GL_UNSIGNED_BYTE, plane->data);
|
||||
}
|
||||
|
||||
struct movie_context *movie_load(const char *filename)
|
||||
{
|
||||
struct movie_context *mc = xcalloc(1, sizeof(struct movie_context));
|
||||
char *path = gamedir_path(filename);
|
||||
mc->plm = plm_create_with_filename(path);
|
||||
if (!mc->plm) {
|
||||
WARNING("Couldn't open %s", path);
|
||||
free(path);
|
||||
movie_free(mc);
|
||||
return NULL;
|
||||
}
|
||||
free(path);
|
||||
|
||||
if (!movie_shader.program)
|
||||
load_movie_shader();
|
||||
|
||||
glGenTextures(3, mc->textures);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
glBindTexture(GL_TEXTURE_2D, mc->textures[i]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
mc->decoder_mutex = SDL_CreateMutex();
|
||||
mc->timer_mutex = SDL_CreateMutex();
|
||||
mc->voice = -1;
|
||||
mc->volume = 100;
|
||||
return mc;
|
||||
}
|
||||
|
||||
void movie_free(struct movie_context *mc)
|
||||
{
|
||||
if (mc->voice >= 0)
|
||||
mixer_stream_stop(mc->voice);
|
||||
|
||||
if (mc->plm)
|
||||
plm_destroy(mc->plm);
|
||||
if (mc->textures[0])
|
||||
glDeleteTextures(3, mc->textures);
|
||||
if (mc->decoder_mutex)
|
||||
SDL_DestroyMutex(mc->decoder_mutex);
|
||||
if (mc->timer_mutex)
|
||||
SDL_DestroyMutex(mc->timer_mutex);
|
||||
free(mc);
|
||||
}
|
||||
|
||||
bool movie_play(struct movie_context *mc)
|
||||
{
|
||||
// Start the audio stream.
|
||||
mc->stream_time = 0.0;
|
||||
mc->wall_time_ms = SDL_GetTicks();
|
||||
mc->sts_stream.userdata = mc;
|
||||
mc->sts_stream.callback = audio_callback;
|
||||
mc->sts_stream.sample.frequency = plm_get_samplerate(mc->plm);
|
||||
mc->sts_stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_FLOAT;
|
||||
mc->voice = mixer_stream_play(&mc->sts_stream, mc->volume);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool movie_draw(struct movie_context *mc, struct sact_sprite *sprite)
|
||||
{
|
||||
// Decode a frame, unless we already have one.
|
||||
plm_frame_t *frame = mc->pending_video_frame;
|
||||
mc->pending_video_frame = NULL;
|
||||
if (!frame) {
|
||||
SDL_LockMutex(mc->decoder_mutex);
|
||||
frame = plm_decode_video(mc->plm);
|
||||
SDL_UnlockMutex(mc->decoder_mutex);
|
||||
if (!frame)
|
||||
return plm_video_has_ended(mc->plm->video_decoder);
|
||||
}
|
||||
|
||||
// If the frame's timestamp is in the future, save the frame and return.
|
||||
SDL_LockMutex(mc->timer_mutex);
|
||||
double now = mc->stream_time + (SDL_GetTicks() - mc->wall_time_ms) / 1000.0;
|
||||
SDL_UnlockMutex(mc->timer_mutex);
|
||||
if (frame->time > now) {
|
||||
mc->pending_video_frame = frame;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Render the frame.
|
||||
update_texture(GL_TEXTURE0, mc->textures[0], &frame->y);
|
||||
update_texture(GL_TEXTURE1, mc->textures[1], &frame->cb);
|
||||
update_texture(GL_TEXTURE2, mc->textures[2], &frame->cr);
|
||||
|
||||
float w, h;
|
||||
GLuint fbo;
|
||||
if (sprite) {
|
||||
struct texture *texture = sprite_get_texture(sprite);
|
||||
w = texture->w;
|
||||
h = texture->h;
|
||||
fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, w, h);
|
||||
} else {
|
||||
// Draw directly to the main framebuffer.
|
||||
w = config.view_width;
|
||||
h = config.view_height;
|
||||
gfx_clear();
|
||||
}
|
||||
|
||||
mat4 world_transform = MAT4(
|
||||
w, 0, 0, 0,
|
||||
0, h, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &movie_shader,
|
||||
.world_transform = world_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = mc
|
||||
};
|
||||
gfx_render(&job);
|
||||
|
||||
if (sprite) {
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
sprite_dirty(sprite);
|
||||
} else {
|
||||
gfx_swap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool movie_is_end(struct movie_context *mc)
|
||||
{
|
||||
return plm_has_ended(mc->plm);
|
||||
}
|
||||
|
||||
int movie_get_position(struct movie_context *mc)
|
||||
{
|
||||
SDL_LockMutex(mc->timer_mutex);
|
||||
int ms = mc->wall_time_ms ? mc->stream_time * 1000 + SDL_GetTicks() - mc->wall_time_ms : 0;
|
||||
SDL_UnlockMutex(mc->timer_mutex);
|
||||
return ms;
|
||||
}
|
||||
|
||||
bool movie_set_volume(struct movie_context *mc, int volume)
|
||||
{
|
||||
mc->volume = volume;
|
||||
if (mc->voice >= 0)
|
||||
mixer_stream_set_volume(mc->voice, volume);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user