#include "bink.h" #include #include #include #include #include #include #include #include #include #include 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 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; 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) { if (aMovie->alInited) { alSourceStop(aMovie->alSource); alDeleteSources(1, &aMovie->alSource); alDeleteBuffers(AUDIO_FRAMES, aMovie->alBuffers); if (aMovie->audioTempBuffer) free(aMovie->audioTempBuffer); } if (aMovie->avContext) avformat_close_input(&aMovie->avContext); if (aMovie->audioCodecContext) avcodec_free_context(&aMovie->audioCodecContext); if (aMovie->audioFrame) av_frame_free(&aMovie->audioFrame); if (aMovie->videoCodecContext) avcodec_free_context(&aMovie->videoCodecContext); 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) { // Initialize scale context. 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); 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) { if (!SoundSys_IsOn()) return 0; if (!aMovie->alInited) { switch (aMovie->audioFrame->channel_layout) { case AV_CH_LAYOUT_MONO: aMovie->alFormat = (aMovie->audioFrame->format == AV_SAMPLE_FMT_U8) ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16; aMovie->alNumChannels = 1; break; case AV_CH_LAYOUT_STEREO: 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; aMovie->audioTempBuffer = malloc(aMovie->alNumChannels * aMovie->audioFrame->nb_samples * 2 * 2); aMovie->alInited = TRUE; } 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>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; if (val > 32767) val = 32767; if (val < -32768) 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; } 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) { // No more packets in file. if (aMovie->looping) { av_seek_frame(aMovie->avContext, -1, 0, 0); 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 decoder’s buffer is full. if (ret == AVERROR(EAGAIN)) { return 1; } else { av_packet_unref(&aMovie->packet); return ret < 0 ? 0 : 1; } } static int BinkStartMovie(struct binkMovie* aMovie, const char* aFilename, BOOL aLoopFlag, BOOL aFmvFlag, BOOL aMusicFlag) { BinkInitMovieStruct(aMovie); aMovie->looping = aLoopFlag; if (aFmvFlag) { aMovie->videoScaleWidth = 128; aMovie->videoScaleHeight = 96; aMovie->videoScaleFormat = AV_PIX_FMT_RGB24; } if (avformat_open_input(&aMovie->avContext, aFilename, NULL, NULL) != 0) return 0; if (avformat_find_stream_info(aMovie->avContext, NULL) < 0) { BinkReleaseMovie(aMovie); return 0; } int numStreams = 0; 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 || avcodec_open2(context, codec, NULL) != 0) { avcodec_free_context(&context); continue; } 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++; } 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++; } else { avcodec_free_context(&context); } } if (aMovie->videoStreamIndex < 0 && aMovie->audioStreamIndex < 0) { BinkReleaseMovie(aMovie); return 0; } if (!aFmvFlag) { for (int i = 0; i < (AUDIO_FRAMES + VIDEO_FRAMES) / 2; i++) ReadPacket(aMovie); } return 1; } static int BinkUpdateMovie(struct binkMovie* aMovie) { if(!aMovie->avContext) return 0; 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); return eof && !playing ? -1 : playing; } 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(); // don’t 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) { if (!SoundSys_IsOn()) return 0; return BinkStartMovie(&musicMovie, filenamePtr, looping, FALSE, TRUE); } int PlayMusicBink(int volume) { if (!SoundSys_IsOn()) return 1; if (!musicMovie.avContext) return 1; 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); if (processedBuffers + musicMovie.alNumFreeBuffers > 0) if (!ReadPacket(&musicMovie)) return 0; } return 1; } void EndMusicBink() { if (SoundSys_IsOn()) return; BinkReleaseMovie(&musicMovie); } //----------------------------------------------------------------------------------------------- FMVHandle CreateBinkFMV(char* filenamePtr) { struct binkMovie* movie = malloc(sizeof(struct binkMovie)); BinkInitMovieStruct(movie); if (!BinkStartMovie(movie, filenamePtr, FALSE, TRUE, FALSE)) { free(movie); return 0; } return (FMVHandle)movie; } int UpdateBinkFMV(FMVHandle aFmvHandle, int volume) { 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) { if (aFmvHandle == 0) return; struct binkMovie* movie = (struct binkMovie*)aFmvHandle; BinkReleaseMovie(movie); free(movie); } char* GetBinkFMVImage(FMVHandle aFmvHandle) { 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; }