#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 FRAMEQUEUESIZE 4 struct binkMovie { AVFormatContext* avContext; AVPacket packet; int videoStreamIndex; AVCodecContext* videoCodecContext; AVFrame* videoFrame; struct SwsContext* videoScaleContext; uint8_t* videoScalePicture[4]; int videoScaleLineSize[4]; uint videoScaleWidth; uint videoScaleHeight; enum AVPixelFormat videoScaleFormat; float videoFrameDuration; int audioStreamIndex; AVCodecContext* audioCodecContext; AVFrame* audioFrame; char* audioTempBuffer; BOOL alInited; ALuint alSource; ALuint alBuffers[FRAMEQUEUESIZE]; ALuint alFreeBuffers[FRAMEQUEUESIZE]; ALuint alNumFreeBuffers; ALuint alNumChannels; ALenum alFormat; ALuint alSampleRate; uint timeStart; BOOL looping; }; static void BinkRenderMovie(struct binkMovie* aMovie) { if (aMovie && aMovie->videoFrame && aMovie->videoScalePicture[0]) { DrawAvpMenuBink( aMovie->videoScalePicture[0], aMovie->videoFrame->width, aMovie->videoFrame->height, aMovie->videoScaleLineSize[0]); } } 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(FRAMEQUEUESIZE, 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); av_freep(&aMovie->videoScalePicture[0]); } BinkInitMovieStruct(aMovie); } static int DecodeVideoFrame(struct binkMovie* aMovie) { if (avcodec_receive_frame(aMovie->videoCodecContext, aMovie->videoFrame) != 0) return 0; 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; av_image_alloc(aMovie->videoScalePicture, 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->videoScalePicture, aMovie->videoScaleLineSize); return 1; } static int DecodeAudioFrame(struct binkMovie* aMovie) { if (avcodec_receive_frame(aMovie->audioCodecContext, aMovie->audioFrame) != 0) return 0; if (!SoundSys_IsOn()) return 0; if (!aMovie->alInited) { alGenSources(1, &aMovie->alSource); AL_CHECK(); alGenBuffers(FRAMEQUEUESIZE, aMovie->alBuffers); AL_CHECK(); 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); alSourcef(aMovie->alSource, AL_GAIN, 1.0); AL_CHECK(); aMovie->alNumFreeBuffers = FRAMEQUEUESIZE; for (int i=0; i < aMovie->alNumFreeBuffers; i++) aMovie->alFreeBuffers[i] = aMovie->alBuffers[i]; 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; } memset(aMovie->audioTempBuffer, 0, aMovie->alNumChannels * aMovie->audioFrame->nb_samples * 2 * 2); if (aMovie->alNumChannels == 0) return 0; // reclaim completed frames int processedBuffers = 0; alGetSourcei(aMovie->alSource, AL_BUFFERS_PROCESSED, &processedBuffers); if (processedBuffers > 0) { alSourceStop(aMovie->alSource); while (processedBuffers > 0) { ALuint buffer = 0; alSourceUnqueueBuffers(aMovie->alSource, 1, &buffer); AL_CHECK(); if (buffer > 0) { aMovie->alFreeBuffers[aMovie->alNumFreeBuffers] = buffer; aMovie->alNumFreeBuffers++; } processedBuffers--; } alSourcePlay(aMovie->alSource); } // queue this frame if (aMovie->alNumFreeBuffers > 0) { 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; } alSourceStop(aMovie->alSource); 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); alSourcePlay(aMovie->alSource); aMovie->alNumFreeBuffers--; aMovie->alFreeBuffers[aMovie->alNumFreeBuffers] = 0; } return 1; } static int ReadFrame(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 ReadFrame(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 = 0; 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->videoFrameDuration = 1000.0f * (float)stream->time_base.num / (float)stream->time_base.den; numStreams++; } else if (aMovie->audioStreamIndex < 0 && context->codec_type == AVMEDIA_TYPE_AUDIO) { aMovie->audioCodecContext = context; aMovie->audioStreamIndex = i; aMovie->audioFrame = av_frame_alloc(); numStreams++; } else { avcodec_free_context(&context); } } if (aMovie->videoStreamIndex < 0 && aMovie->audioStreamIndex < 0) { BinkReleaseMovie(aMovie); return 0; } if (!aFmvFlag) { for (int i = 0; i < (FRAMEQUEUESIZE-1) * numStreams; i++) ReadFrame(aMovie); } return 1; } static int BinkUpdateMovie(struct binkMovie* aMovie) { if(!aMovie->avContext) return 0; const int t = SDL_GetTicks(); const int eof = !ReadFrame(aMovie); int playing = 0; if (aMovie->videoStreamIndex >= 0) { if (aMovie->videoFrame->pts == 0) aMovie->timeStart = t - aMovie->videoFrameDuration; if (t - aMovie->timeStart >= aMovie->videoFrame->pts * aMovie->videoFrameDuration) playing += DecodeVideoFrame(aMovie); } if (aMovie->audioStreamIndex >= 0) { int processedBuffers = 0; alGetSourcei(aMovie->alSource, AL_BUFFERS_PROCESSED, &processedBuffers); while (!aMovie->alInited || aMovie->alNumFreeBuffers>0 || processedBuffers>0) { if (DecodeAudioFrame(aMovie)) playing += 1; else break; } } return !eof || playing; } void PlayBinkedFMV(char* filenamePtr, int volume) { struct binkMovie movie; if (BinkStartMovie(&movie, filenamePtr, FALSE, FALSE, FALSE)) { alSourcef(movie.alSource, AL_GAIN, PlatVolumeToGain(volume)); while (BinkUpdateMovie(&movie)) { BinkRenderMovie(&movie); FlipBuffers(); SDL_Delay(4); // don’t just burn it } BinkReleaseMovie(&movie); } } //----------------------------------------------------------------------------------------------- struct binkMovie menuBackgroundMovie; void StartMenuBackgroundBink() { BinkStartMovie(&menuBackgroundMovie, "FMVs/Menubackground.bik", TRUE, FALSE, FALSE); } int PlayMenuBackgroundBink() { ClearScreenToBlack(); if (BinkUpdateMovie(&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 * FRAMEQUEUESIZE; i++) { int processedBuffers = 0; alGetSourcei(musicMovie.alSource, AL_BUFFERS_PROCESSED, &processedBuffers); if (processedBuffers + musicMovie.alNumFreeBuffers > 0) if (!ReadFrame(&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 0; struct binkMovie* movie = (struct binkMovie*)aFmvHandle; if(!movie->videoScaleContext) return 0; return movie->videoScalePicture[0]; }