avp/src/bink.c

554 lines
15 KiB
C
Raw Normal View History

2020-05-28 16:04:01 +02:00
#include "bink.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <AL/al.h>
#include <SDL.h>
2020-05-28 16:04:01 +02:00
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
extern int SoundSys_IsOn();
extern void DrawAvpMenuBink(char* buf, int width, int height, int pitch);
extern float PlatVolumeToGain(int volume);
//#define AL_CHECK() { int err = alGetError(); if(err!=AL_NO_ERROR) printf("%s:%d ALError %04x\n", __FILE__, __LINE__, err); }
#define AL_CHECK() {}
#define AUDIO_FRAMES 32
#define VIDEO_FRAMES 4
2020-05-28 16:04:01 +02:00
struct binkMovie {
AVFormatContext* avContext;
AVPacket packet;
int videoStreamIndex;
AVCodecContext* videoCodecContext;
AVFrame* videoFrame;
struct SwsContext* videoScaleContext;
int videoScaleLineSize[4];
uint videoScaleWidth;
uint videoScaleHeight;
enum AVPixelFormat videoScaleFormat;
uint8_t* videoFrames[VIDEO_FRAMES][4];
int currentFrame;
int renderedFrames;
float frameDuration;
int audioStreamIndex;
AVCodecContext* audioCodecContext;
AVFrame* audioFrame;
char* audioTempBuffer;
BOOL alInited;
ALuint alSource;
ALuint alBuffers[AUDIO_FRAMES];
ALuint alFreeBuffers[AUDIO_FRAMES];
ALuint alNumFreeBuffers;
ALuint alNumChannels;
2020-05-28 16:04:01 +02:00
ALenum alFormat;
ALuint alSampleRate;
uint timeStart;
BOOL looping;
};
static int BinkRenderMovie(struct binkMovie* movie)
{
const int t = SDL_GetTicks() - movie->timeStart;
int dt = 0;
while (movie->renderedFrames > 1 &&
(dt = t - (movie->currentFrame + 1) * movie->frameDuration) >= 0) {
movie->currentFrame++;
movie->renderedFrames--;
}
if (movie->renderedFrames) {
DrawAvpMenuBink(movie->videoFrames[movie->currentFrame%VIDEO_FRAMES][0],
movie->videoFrame->width,
movie->videoFrame->height,
movie->videoScaleLineSize[0]);
}
return dt;
}
static void BinkInitMovieStruct(struct binkMovie* aMovie)
{
*aMovie = (struct binkMovie){
.audioStreamIndex = -1,
.videoStreamIndex = -1,
.videoScaleFormat = AV_PIX_FMT_NONE,
};
}
static void BinkReleaseMovie(struct binkMovie* aMovie)
{
2020-05-28 16:04:01 +02:00
if (aMovie->alInited) {
alSourceStop(aMovie->alSource);
alDeleteSources(1, &aMovie->alSource);
alDeleteBuffers(AUDIO_FRAMES, aMovie->alBuffers);
2020-05-28 16:04:01 +02:00
if (aMovie->audioTempBuffer)
free(aMovie->audioTempBuffer);
}
2020-05-28 16:04:01 +02:00
if (aMovie->avContext)
avformat_close_input(&aMovie->avContext);
2020-05-28 16:04:01 +02:00
if (aMovie->audioCodecContext)
avcodec_free_context(&aMovie->audioCodecContext);
2020-05-28 16:04:01 +02:00
if (aMovie->audioFrame)
av_frame_free(&aMovie->audioFrame);
2020-05-28 16:04:01 +02:00
if (aMovie->videoCodecContext)
avcodec_free_context(&aMovie->videoCodecContext);
2020-05-28 16:04:01 +02:00
if (aMovie->videoFrame)
av_frame_free(&aMovie->videoFrame);
if (aMovie->videoScaleContext) {
sws_freeContext(aMovie->videoScaleContext);
for (int i = 0; i < VIDEO_FRAMES; i++)
av_freep(&aMovie->videoFrames[i][0]);
}
BinkInitMovieStruct(aMovie);
}
static int DecodeVideoFrame(struct binkMovie* aMovie)
{
2020-05-31 13:51:44 +02:00
// Initialize scale context.
2020-05-28 16:04:01 +02:00
if (aMovie->videoScaleContext == NULL) {
if (aMovie->videoScaleWidth == 0)
aMovie->videoScaleWidth = aMovie->videoFrame->width;
if (aMovie->videoScaleHeight == 0)
aMovie->videoScaleHeight = aMovie->videoFrame->height;
if (aMovie->videoScaleFormat == AV_PIX_FMT_NONE)
aMovie->videoScaleFormat = AV_PIX_FMT_RGB565;
aMovie->videoScaleContext = sws_getContext(
aMovie->videoFrame->width, aMovie->videoFrame->height,
aMovie->videoFrame->format,
aMovie->videoScaleWidth, aMovie->videoScaleHeight,
aMovie->videoScaleFormat,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
2020-05-28 16:04:01 +02:00
if (aMovie->videoScaleContext == NULL)
return 0;
for (int i = 0; i < VIDEO_FRAMES; i++) {
av_image_alloc(
aMovie->videoFrames[i], aMovie->videoScaleLineSize,
aMovie->videoScaleWidth, aMovie->videoScaleHeight,
aMovie->videoScaleFormat, 1);
}
}
sws_scale(aMovie->videoScaleContext,
(const uint8_t* const*)aMovie->videoFrame->data,
aMovie->videoFrame->linesize, 0, aMovie->videoFrame->height,
aMovie->videoFrames[(aMovie->currentFrame+aMovie->renderedFrames) % VIDEO_FRAMES],
aMovie->videoScaleLineSize);
aMovie->renderedFrames++;
return 1;
}
// Reclaim completed audio buffers.
static int ProcessAudio(struct binkMovie* aMovie)
{
if (!aMovie->alInited)
return 0;
int processed = 0;
alGetSourcei(aMovie->alSource, AL_BUFFERS_PROCESSED, &processed);
if (processed > 0) {
ALuint buffers[AUDIO_FRAMES];
alSourceUnqueueBuffers(aMovie->alSource, processed, buffers);
for (int i = 0; i < processed; i++)
aMovie->alFreeBuffers[aMovie->alNumFreeBuffers++] = buffers[i];
}
return 1;
}
static int DecodeAudioFrame(struct binkMovie* aMovie)
{
2020-05-28 16:04:01 +02:00
if (!SoundSys_IsOn())
return 0;
2020-05-28 16:04:01 +02:00
if (!aMovie->alInited) {
switch (aMovie->audioFrame->channel_layout) {
case AV_CH_LAYOUT_MONO:
2020-05-28 16:04:01 +02:00
aMovie->alFormat = (aMovie->audioFrame->format == AV_SAMPLE_FMT_U8) ?
AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
aMovie->alNumChannels = 1;
break;
case AV_CH_LAYOUT_STEREO:
2020-05-28 16:04:01 +02:00
aMovie->alFormat = (aMovie->audioFrame->format == AV_SAMPLE_FMT_U8) ?
AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
aMovie->alNumChannels = 2;
break;
}
aMovie->alSampleRate = aMovie->audioFrame->sample_rate;
2020-05-28 16:04:01 +02:00
aMovie->audioTempBuffer =
malloc(aMovie->alNumChannels * aMovie->audioFrame->nb_samples * 2 * 2);
aMovie->alInited = TRUE;
}
2020-05-28 16:04:01 +02:00
if (aMovie->alNumChannels == 0)
return 0;
if (aMovie->alNumFreeBuffers == 0)
return 0;
// queue this frame
ALuint alBuffer = aMovie->alFreeBuffers[aMovie->alNumFreeBuffers-1];
int sampleCount = aMovie->audioFrame->nb_samples * aMovie->alNumChannels;
uint dataSize = sampleCount * 2; // 16bit is deafult
void* data = (void*)aMovie->audioFrame->extended_data[0];
switch (aMovie->audioFrame->format) {
case AV_SAMPLE_FMT_U8: {
dataSize = sampleCount;
} break;
default:
case AV_SAMPLE_FMT_S16: {
/*
unsigned short* p = (unsigned short*) data;
for(int i=0; i<sampleCount; i++)
p[i] -= p[i]>>2;
*/
} break;
case AV_SAMPLE_FMT_FLT: {
data = (void*)aMovie->audioTempBuffer;
short* tempBuf = (short*)aMovie->audioTempBuffer;
float* srcBuf = (float*)aMovie->audioFrame->extended_data[0];
for (int i = 0; i < sampleCount; i++) {
float val = srcBuf[i] * 32768;
if (val > 32767)
val = 32767;
if (val < -32768)
val = -32768;
tempBuf[i] = (short)val;
}
} break;
case AV_SAMPLE_FMT_S32: {
data = (void*)aMovie->audioTempBuffer;
short* tempBuf = (short*)aMovie->audioTempBuffer;
unsigned int* srcBuf = (unsigned int*)aMovie->audioFrame->extended_data[0];
for(int i = 0; i < sampleCount; i++)
tempBuf[i] = (short)(((*srcBuf - *srcBuf>>2) >> 16) & 0x0000FFFF);
} break;
case AV_SAMPLE_FMT_FLTP: {
data = (void*)aMovie->audioTempBuffer;
short* tempBuf = (short*)aMovie->audioTempBuffer;
for (int i = 0; i < aMovie->audioFrame->nb_samples; i++) {
for (int j = 0; j < aMovie->alNumChannels; j++) {
float* srcBuf = (float*)aMovie->audioFrame->extended_data[j];
float val = srcBuf[i] * 32768;
2020-05-28 16:04:01 +02:00
if (val > 32767)
val = 32767;
if (val < -32768)
2020-05-31 10:54:52 +02:00
val = -32768;
tempBuf[(i*aMovie->alNumChannels)+j] = (short)val;
}
}
} break;
}
alBufferData(alBuffer, aMovie->alFormat, data, dataSize - 16, aMovie->alSampleRate);
AL_CHECK();
alSourceQueueBuffers(aMovie->alSource, 1, &alBuffer);
AL_CHECK();
float vx, vy, vz;
alGetListener3f(AL_VELOCITY, &vx, &vy, &vz);
alSource3f(aMovie->alSource, AL_VELOCITY, vx, vy, vz);
int state = 0;
alGetSourcei(aMovie->alSource, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING)
alSourcePlay(aMovie->alSource);
aMovie->alNumFreeBuffers--;
aMovie->alFreeBuffers[aMovie->alNumFreeBuffers] = 0;
return 1;
}
2020-05-31 13:02:27 +02:00
static int ReadPacket(struct binkMovie* aMovie)
{
// Read from file if no packet is buffered.
if (!aMovie->packet.buf && av_read_frame(aMovie->avContext, &aMovie->packet) < 0) {
2020-05-28 16:04:01 +02:00
// No more packets in file.
if (aMovie->looping) {
av_seek_frame(aMovie->avContext, -1, 0, 0);
2020-05-31 13:02:27 +02:00
return ReadPacket(aMovie);
} else {
// Drain buffered frames.
if (aMovie->videoStreamIndex >= 0)
avcodec_send_packet(aMovie->videoCodecContext, NULL);
if (aMovie->audioStreamIndex >= 0)
avcodec_send_packet(aMovie->audioCodecContext, NULL);
return 0;
}
}
// Send the (possibly buffered) packet to decoder.
int ret = AVERROR(EAGAIN);
if (aMovie->packet.stream_index == aMovie->videoStreamIndex)
ret = avcodec_send_packet(aMovie->videoCodecContext, &aMovie->packet);
else if (aMovie->packet.stream_index == aMovie->audioStreamIndex)
ret = avcodec_send_packet(aMovie->audioCodecContext, &aMovie->packet);
// Keep the packet around for next time if decoders buffer is full.
if (ret == AVERROR(EAGAIN)) {
return 1;
} else {
av_packet_unref(&aMovie->packet);
return ret < 0 ? 0 : 1;
}
}
2020-05-28 16:04:01 +02:00
static int BinkStartMovie(struct binkMovie* aMovie, const char* aFilename,
BOOL aLoopFlag, BOOL aFmvFlag, BOOL aMusicFlag)
{
BinkInitMovieStruct(aMovie);
aMovie->looping = aLoopFlag;
2020-05-28 16:04:01 +02:00
if (aFmvFlag) {
aMovie->videoScaleWidth = 128;
aMovie->videoScaleHeight = 96;
aMovie->videoScaleFormat = AV_PIX_FMT_RGB24;
}
2020-05-28 16:04:01 +02:00
if (avformat_open_input(&aMovie->avContext, aFilename, NULL, NULL) != 0)
return 0;
2020-05-28 16:04:01 +02:00
if (avformat_find_stream_info(aMovie->avContext, NULL) < 0) {
BinkReleaseMovie(aMovie);
return 0;
}
int numStreams = 0;
2020-05-28 16:04:01 +02:00
for (int i = 0; i < aMovie->avContext->nb_streams; i++) {
const AVStream* stream = aMovie->avContext->streams[i];
const AVCodec* codec = avcodec_find_decoder(stream->codecpar->codec_id);
if (!codec)
continue;
AVCodecContext *context = avcodec_alloc_context3(codec);
if (!context)
continue;
if (avcodec_parameters_to_context(context, stream->codecpar) < 0 ||
2020-05-28 16:04:01 +02:00
avcodec_open2(context, codec, NULL) != 0) {
avcodec_free_context(&context);
continue;
}
2020-05-28 16:04:01 +02:00
if (aMovie->videoStreamIndex < 0 && context->codec_type == AVMEDIA_TYPE_VIDEO) {
aMovie->videoCodecContext = context;
aMovie->videoStreamIndex = i;
aMovie->videoFrame = av_frame_alloc();
aMovie->frameDuration =
1000.0f * (float)stream->time_base.num / (float)stream->time_base.den;
aMovie->timeStart = SDL_GetTicks();
numStreams++;
2020-05-28 16:04:01 +02:00
} else if (aMovie->audioStreamIndex < 0 && context->codec_type == AVMEDIA_TYPE_AUDIO) {
aMovie->audioCodecContext = context;
aMovie->audioStreamIndex = i;
aMovie->audioFrame = av_frame_alloc();
alGenSources(1, &aMovie->alSource);
alSource3f(aMovie->alSource, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(aMovie->alSource, AL_VELOCITY, 0.0, 0.0, 0.0);
alSource3f(aMovie->alSource, AL_DIRECTION, 0.0, 0.0, 0.0);
alSourcef(aMovie->alSource, AL_ROLLOFF_FACTOR, 0.0);
alSourcei(aMovie->alSource, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcef(aMovie->alSource, AL_PITCH, 1.0);
alSourcef(aMovie->alSource, AL_GAIN, 1.0);
alGenBuffers(AUDIO_FRAMES, aMovie->alBuffers);
aMovie->alNumFreeBuffers = AUDIO_FRAMES;
for (int i = 0; i < aMovie->alNumFreeBuffers; i++)
aMovie->alFreeBuffers[i] = aMovie->alBuffers[i];
numStreams++;
2020-05-28 16:04:01 +02:00
} else {
avcodec_free_context(&context);
}
}
2020-05-28 16:04:01 +02:00
if (aMovie->videoStreamIndex < 0 && aMovie->audioStreamIndex < 0) {
BinkReleaseMovie(aMovie);
return 0;
}
2020-05-28 16:04:01 +02:00
if (!aFmvFlag) {
for (int i = 0; i < (AUDIO_FRAMES + VIDEO_FRAMES) / 2; i++)
2020-05-31 13:02:27 +02:00
ReadPacket(aMovie);
}
return 1;
}
static int BinkUpdateMovie(struct binkMovie* aMovie)
{
if(!aMovie->avContext)
return 0;
2020-05-31 13:02:27 +02:00
const int eof = !ReadPacket(aMovie);
int playing = 0;
if (aMovie->videoStreamIndex >= 0 && aMovie->renderedFrames < VIDEO_FRAMES)
if (avcodec_receive_frame(aMovie->videoCodecContext, aMovie->videoFrame) == 0)
playing += DecodeVideoFrame(aMovie);
if (aMovie->audioStreamIndex >= 0 && aMovie->alNumFreeBuffers > 0)
if (avcodec_receive_frame(aMovie->audioCodecContext, aMovie->audioFrame) == 0)
playing += DecodeAudioFrame(aMovie);
2020-05-28 16:04:01 +02:00
return eof && !playing ? -1 : playing;
}
2020-05-28 16:04:01 +02:00
void PlayBinkedFMV(char* filenamePtr, int volume)
{
struct binkMovie movie;
if (BinkStartMovie(&movie, filenamePtr, FALSE, FALSE, FALSE)) {
alSourcef(movie.alSource, AL_GAIN, PlatVolumeToGain(volume));
int updated = 0;
while ((updated = BinkUpdateMovie(&movie)) >= 0) {
ProcessAudio(&movie);
int dt = BinkRenderMovie(&movie);
FlipBuffers();
// dont just burn it
if (!updated && dt > 0)
SDL_Delay(dt);
}
BinkReleaseMovie(&movie);
}
}
//-----------------------------------------------------------------------------------------------
struct binkMovie menuBackgroundMovie;
void StartMenuBackgroundBink()
{
BinkStartMovie(&menuBackgroundMovie, "FMVs/Menubackground.bik", TRUE, FALSE, FALSE);
}
int PlayMenuBackgroundBink()
{
ClearScreenToBlack();
if (BinkUpdateMovie(&menuBackgroundMovie) >= 0) {
ProcessAudio(&menuBackgroundMovie);
BinkRenderMovie(&menuBackgroundMovie);
return 1;
}
return 0;
}
void EndMenuBackgroundBink()
{
BinkReleaseMovie(&menuBackgroundMovie);
}
//-----------------------------------------------------------------------------------------------
struct binkMovie musicMovie;
int StartMusicBink(char* filenamePtr, BOOL looping)
{
2020-05-28 16:04:01 +02:00
if (!SoundSys_IsOn())
return 0;
2020-05-28 16:04:01 +02:00
return BinkStartMovie(&musicMovie, filenamePtr, looping, FALSE, TRUE);
}
int PlayMusicBink(int volume)
{
2020-05-28 16:04:01 +02:00
if (!SoundSys_IsOn())
return 1;
2020-05-28 16:04:01 +02:00
if (!musicMovie.avContext)
return 1;
2020-05-28 16:04:01 +02:00
if (musicMovie.audioStreamIndex < 0 || !musicMovie.alInited)
return 1;
alSourcef(musicMovie.alSource, AL_GAIN, PlatVolumeToGain(volume));
for (int i = 0; i < musicMovie.avContext->nb_streams * AUDIO_FRAMES; i++) {
int processedBuffers = 0;
alGetSourcei(musicMovie.alSource, AL_BUFFERS_PROCESSED, &processedBuffers);
2020-05-28 16:04:01 +02:00
if (processedBuffers + musicMovie.alNumFreeBuffers > 0)
2020-05-31 13:02:27 +02:00
if (!ReadPacket(&musicMovie))
return 0;
}
return 1;
}
void EndMusicBink()
{
2020-05-28 16:04:01 +02:00
if (SoundSys_IsOn())
return;
BinkReleaseMovie(&musicMovie);
}
//-----------------------------------------------------------------------------------------------
FMVHandle CreateBinkFMV(char* filenamePtr)
{
struct binkMovie* movie = malloc(sizeof(struct binkMovie));
BinkInitMovieStruct(movie);
2020-05-28 16:04:01 +02:00
if (!BinkStartMovie(movie, filenamePtr, FALSE, TRUE, FALSE)) {
free(movie);
return 0;
}
return (FMVHandle)movie;
}
int UpdateBinkFMV(FMVHandle aFmvHandle, int volume)
{
2020-05-28 16:04:01 +02:00
if (aFmvHandle == 0)
return 0;
struct binkMovie* movie = (struct binkMovie*)aFmvHandle;
alSourcef(movie->alSource, AL_GAIN, PlatVolumeToGain(volume));
BinkUpdateMovie(movie);
BinkUpdateMovie(movie);
BinkUpdateMovie(movie);
BinkUpdateMovie(movie);
return BinkUpdateMovie(movie);
}
void CloseBinkFMV(FMVHandle aFmvHandle)
{
2020-05-28 16:04:01 +02:00
if (aFmvHandle == 0)
return;
struct binkMovie* movie = (struct binkMovie*)aFmvHandle;
BinkReleaseMovie(movie);
free(movie);
}
char* GetBinkFMVImage(FMVHandle aFmvHandle)
{
2020-05-28 16:04:01 +02:00
if (aFmvHandle == 0)
return NULL;
struct binkMovie* movie = (struct binkMovie*)aFmvHandle;
if (!movie->videoScaleContext)
return NULL;
return movie->renderedFrames ?
movie->videoFrames[movie->currentFrame%VIDEO_FRAMES][0] : NULL;
}