#include #include #include #include "fixer.h" #include "bink.h" #include #include #include #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/avutil.h" #include "libavutil/imgutils.h" #include "libavutil/channel_layout.h" #include "libswscale/swscale.h" //#define DISABLE_MOVIES //#define DISABLE_MUSIC //#define DISABLE_FMVS extern void SDL_Delay(); extern uint SDL_GetTicks(); 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; BOOL isfmv; }; //----------------------------------------------------------------------------------------------- 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->videoScaleContext) av_freep(&aMovie->videoScalePicture[0]); 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); BinkInitMovieStruct(aMovie); } static int DecodeVideoFrame(struct binkMovie* aMovie) { int ret = avcodec_receive_frame(aMovie->videoCodecContext, aMovie->videoFrame); if (ret < 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) { int decoded_frame_ready = 0; av_frame_unref(aMovie->audioFrame); //avcodec_get_frame_defaults(aMovie->audioFrame); int ret = avcodec_receive_frame(aMovie->audioCodecContext, aMovie->audioFrame); if (ret < 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; ialNumFreeBuffers; 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; // 16bit is deafult uint dataSize = sampleCount*2; 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 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>2) >> 16) & 0x0000FFFF); } break; case AV_SAMPLE_FMT_FLTP: { data = (void*) aMovie->audioTempBuffer; short* tempBuf = (short*) aMovie->audioTempBuffer; for(int i=0; iaudioFrame->nb_samples; i++) { for(int j=0; jalNumChannels; 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); //printf("fmt=%d, buffer size=%d, rdy=%d, len=%d, s1=%d, samples=%d\n", aMovie->audioFrame->format, dataSize, decoded_frame_ready, len, aPacket->size, sampleCount); 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) { 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; iavContext->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; int ret = BinkStartMovie(&musicMovie, filenamePtr, looping, FALSE, TRUE); return ret; } 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; inb_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]; }