Mercurial > mm7
view Media/MediaPlayer.cpp @ 2516:56b6d74ce716
.
author | Ritor1 |
---|---|
date | Thu, 02 Oct 2014 22:01:06 +0600 |
parents | ae2b7048b3eb |
children | b36b858a5175 |
line wrap: on
line source
#define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #include <vector> #include <deque> #define _CRT_SECURE_NO_WARNINGS #include "IO/Mouse.h" #include "GUI/GUIWindow.h" #include "Engine/mm7_data.h" #include "Media/Audio/OpenALSoundProvider.h" #include "Engine/Log.h" #include "MediaPlayer.h" #include "Media/Video/Bink_Smacker.h" #include "Media/Audio/AudioPlayer.h" #include "Engine/Timer.h" #include "Engine/Graphics/Render.h" #include "Engine/Game.h" #include "Engine/MMT.h" #pragma comment(lib, "Version.lib") using namespace Media; Media::MPlayer *pMediaPlayer = nullptr; Media::IMovie *pMovie_Track; Media::ITrack *pAudio_Track; Movie *movie; int mSourceID; void PlayMovie(const wchar_t * pFilename); void PlayAudio(const wchar_t * pFilename); void LoadMovie(const char *); class MemoryStream { public: inline MemoryStream(void *data, size_t data_size) { this->data_size = data_size; this->data = data; this->current_pos = 0; } inline MemoryStream() { this->data_size = 0; this->data = nullptr; this->current_pos = 0; } inline ~MemoryStream() { //Log::Warning(L"Destructor: data delete %u", data); if (data) delete [] data; } inline size_t Write(void *buffer, size_t num_bytes) { if (!data) { data_size = 32 + num_bytes; data = new char[data_size]; //Log::Warning(L"new data %u", data); current_pos = 0; } else if (current_pos + num_bytes >= data_size) { int new_data_size = data_size + num_bytes + data_size / 8 + 4; auto new_data = new char[new_data_size]; //Log::Warning(L"new new_data %u", new_data); memcpy(new_data, data, data_size); //Log::Warning(L"data delete %u", data); delete [] data; data_size = new_data_size; data = new_data; } memcpy((char *)data + current_pos, buffer, num_bytes); current_pos += num_bytes; return num_bytes; } inline size_t Read(void *buffer, size_t num_bytes) { size_t read_size = min(num_bytes, data_size - current_pos); if (read_size) { memcpy(buffer, (char *)data + current_pos, read_size); current_pos += read_size; } return read_size; } inline void Reset() { current_pos = 0; } inline void SeekToEnd() { current_pos = data_size; } inline size_t Unwind(size_t bytes) { if (bytes > current_pos) current_pos = 0; else current_pos -= bytes; return current_pos; } inline size_t Rewind(size_t bytes) { if (current_pos + bytes >= data_size) current_pos = data_size; else current_pos += bytes; return current_pos; } inline size_t Size() const {return data_size;} inline size_t Current() const {return current_pos;} inline void *Ptr() const {return data;} private: void *data; size_t data_size; size_t current_pos; }; OpenALSoundProvider *provider = nullptr; static int av_num_bytes_per_sample(AVSampleFormat sample_fmt) { switch (sample_fmt) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: return 1; case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: return 2; case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP: return 4; case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBLP: return 8; default: case AV_SAMPLE_FMT_NONE: Error("Invalid av sample format: %u", sample_fmt); } return 0; } struct AVStreamWrapper { inline AVStreamWrapper() { this->type = AVMEDIA_TYPE_UNKNOWN; this->stream_idx = -1; this->stream = nullptr; this->dec = nullptr; this->dec_ctx = nullptr; } inline void Release() { type = AVMEDIA_TYPE_UNKNOWN; stream_idx = -1; stream = nullptr; dec = nullptr; if (dec_ctx) { // Close the codec avcodec_close(dec_ctx); Log::Warning(L"close decoder context file\n"); dec_ctx = nullptr; } } AVMediaType type; int stream_idx; AVStream *stream; AVCodec *dec; AVCodecContext *dec_ctx; }; struct AVAudioStream: public AVStreamWrapper { inline AVAudioStream():AVStreamWrapper() { this->bytes_per_sample = 0; this->bytes_per_second = 0; } int bytes_per_sample; int bytes_per_second; }; struct AVVideoStream: public AVStreamWrapper { inline AVVideoStream(): AVStreamWrapper() { this->frames_per_second = 0.0; } double frames_per_second; }; static bool av_open_stream(AVFormatContext *format_ctx, AVMediaType type, AVStreamWrapper *out_stream) { int stream_idx = av_find_best_stream(format_ctx, type, -1, -1, nullptr, 0); if (stream_idx >= 0) { auto stream = format_ctx->streams[stream_idx]; auto dec_ctx = stream->codec; // Find the decoder for the video stream auto dec = avcodec_find_decoder(dec_ctx->codec_id); if (dec) { // Open codec if (avcodec_open2(dec_ctx, dec, nullptr) >= 0) { out_stream->type = type; out_stream->stream_idx = stream_idx; out_stream->stream = stream; out_stream->dec = dec; out_stream->dec_ctx = dec_ctx; return true; } fprintf(stderr, "ffmpeg: Could not open codec\n"); return false; } fprintf(stderr, "ffmpeg: Unable to open codec\n"); return false; } fprintf(stderr, "ffmpeg: Didn't find a stream\n"); return false; } static bool av_open_audio_stream(AVFormatContext *format_ctx, AVAudioStream *out_stream) { if (av_open_stream(format_ctx, AVMEDIA_TYPE_AUDIO, out_stream)) //return Error("Audio stream not found"), false; // we support only 2-channel audio for now //if (out_stream->dec_ctx->channels != 2) //{ // out_stream->Release(); // return Error("Unsupported number of channels: %u", out_stream->dec_ctx->channels), false; //} { out_stream->bytes_per_sample = av_num_bytes_per_sample(out_stream->dec_ctx->sample_fmt); out_stream->bytes_per_second = out_stream->dec_ctx->channels * out_stream->dec_ctx->sample_rate * out_stream->bytes_per_sample; } return true; } static bool av_open_video_stream(AVFormatContext *format_ctx, AVVideoStream *out_stream) { if (!av_open_stream(format_ctx, AVMEDIA_TYPE_VIDEO, out_stream)) return Error("Video stream not found"), false; out_stream->frames_per_second = (double)out_stream->dec_ctx->time_base.den / (double)out_stream->dec_ctx->time_base.num; return true; } void InterleaveAudioData(MemoryStream *stream, AVSampleFormat src_format, int num_channels, int num_samples, uint8_t **channels) { unsigned int bytes_per_sample; switch (src_format) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: __debugbreak(); case AV_SAMPLE_FMT_S16: bytes_per_sample = sizeof(__int16); stream->Write(channels[0], num_channels * num_samples * bytes_per_sample); break; case AV_SAMPLE_FMT_S16P: { bytes_per_sample = sizeof(__int16); for (int i = 0; i < num_samples; ++i) for (int j = 0; j < num_channels; ++j) stream->Write(channels[j] + i * bytes_per_sample, bytes_per_sample); } break; case AV_SAMPLE_FMT_FLT: { SwrContext *converter = swr_alloc(); av_opt_set_int(converter, "in_channel_layout", av_get_default_channel_layout(2), 0); //av_opt_set_int(converter, "in_sample_rate", sample_ra, 0); av_opt_set_sample_fmt(converter, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0); av_opt_set_int(converter, "out_channel_layout", av_get_default_channel_layout(2), 0); //av_opt_set_int(converter, "out_sample_rate", dst_sample_rate, 0); av_opt_set_sample_fmt(converter, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); if (swr_init(converter) < 0) { __debugbreak(); swr_free(&converter); return; } uint8_t **dst_channels; int dst_linesize[8]; //int dst_nb_channels = av_get_channel_layout_nb_channels(dst_channel_layout); if (av_samples_alloc_array_and_samples(&dst_channels, dst_linesize, 2, num_channels * num_samples, AV_SAMPLE_FMT_S16, 0) < 0) { __debugbreak(); swr_free(&converter); return; } if (swr_convert(converter, dst_channels, num_channels * num_samples, (const uint8_t **)channels, num_channels * num_samples) >= 0) //Invalid partial memory access, Uninitialized memory access stream->Write(dst_channels[0], num_channels * num_samples * sizeof(__int16)); else __debugbreak(); av_free(dst_channels[0]); swr_free(&converter); } break; default: __debugbreak(); //if (Resample(next_frame->avframe, next_frame->avframe->channel_layout, next_frame->avframe->sample_rate, // av_get_default_channel_layout(2), next_frame->avframe->sample_rate, AV_SAMPLE_FMT_S16P, resampled_data)) } } const uint16_t ff_wma_critical_freqs[25] = { 100, 200, 300, 400, 510, 630, 770, 920, 1080, 1270, 1480, 1720, 2000, 2320, 2700, 3150, 3700, 4400, 5300, 6400, 7700, 9500, 12000, 15500, 24500, }; extern const uint16_t ff_wma_critical_freqs[25]; static float quant_table[96]; bool DecodeAudioFrame(AVCodecContext *dec_ctx, AVPacket *avpacket, AVFrame *avframe, MemoryStream *out_audio_data, int *out_num_audio_samples) { volatile int decoded = false; do { if (avcodec_decode_audio4(dec_ctx, avframe, (int *)&decoded, avpacket) < 0) //Uninitialized portail memory access { log("Cannot decode audio frame\n"); return false; } if (!decoded) { log("Cannot decode audio frame in one piece\n"); break; } } while (!decoded); switch (dec_ctx->codec_id) { case AV_CODEC_ID_BINKAUDIO_DCT: { __debugbreak(); } case AV_CODEC_ID_BINKAUDIO_RDFT: {//pts samples dpts // 0 960 //17280 960 17280 18x960 //18240 960 960 1x960 //20160 960 1920 2x960 //21120 960 960 1x960 //23040 960 1920 2x960 /*static int bink_next_pts = 0; // there's a gap in the sound - fill empty samples in if (bink_next_pts < avpacket->pts) { short silence[1024]; memset(silence, 0, sizeof(silence)); int samples_to_fill = /*dec_ctx->channels * (avpacket->pts - bink_next_pts); while (samples_to_fill > 0) { int samples_to_fill_this_step = samples_to_fill >= 1024 ? 1024 : samples_to_fill; out_audio_data->Write(silence, samples_to_fill_this_step * sizeof(short)); samples_to_fill -= samples_to_fill_this_step; } } bink_next_pts = avpacket->pts + /*dec_ctx->channels * avframe->nb_samples; */ AVFrame frame; int first; int version_b; int frame_len; int overlap_len; int block_size; int num_bands; unsigned int *bands; float root; int sample_rate = dec_ctx->sample_rate; int sample_rate_half; int i; int frame_len_bits; int channels; //compresses audio in chunks of varying sizes depending on sample rate: // if sample rate < 22050, frame size is 2048 samples // if sample rate < 44100, frame size is 4096 samples // else, frame size is 8192 samples /* determine frame length */ if (dec_ctx->sample_rate < 22050) frame_len_bits = 9; else if (dec_ctx->sample_rate < 44100) frame_len_bits = 10; else frame_len_bits = 11; if (dec_ctx->channels < 1 || dec_ctx->channels > 2) { av_log(dec_ctx, AV_LOG_ERROR, "invalid number of channels: %d\n", dec_ctx->channels); return AVERROR_INVALIDDATA; } version_b = dec_ctx->extradata_size >= 4 && dec_ctx->extradata[3] == 'b'; if (version_b) __debugbreak(); if (dec_ctx->codec->id == AV_CODEC_ID_BINKAUDIO_RDFT) { // audio is already interleaved for the RDFT format variant dec_ctx->sample_fmt = AV_SAMPLE_FMT_FLT; sample_rate *= dec_ctx->channels; channels = 1; if (!version_b) frame_len_bits += av_log2(dec_ctx->channels); } else { channels = dec_ctx->channels; dec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; } frame_len = 1 << frame_len_bits; //2048 //a frame is windowed with the previous frame; the size of the window is frame size / 16 overlap_len = frame_len / 16; //128 block_size = (frame_len - overlap_len) * channels; //1920 //compute half the sample rate as (sample rate + 1) / 2; //initialize an array of band frequencies corresponding to an array of 25 critical frequencies (same as WMA, apparently), // any for which the critical frequencies are less than half the sample rate sample_rate_half = (sample_rate + 1) / 2; //22050 if (dec_ctx->codec->id == AV_CODEC_ID_BINKAUDIO_RDFT) root = 2.0 / (sqrt(float(frame_len)) * 32768.0); else root = frame_len / (sqrt(float(frame_len)) * 32768.0); for (i = 0; i < 96; i++) { /* constant is result of 0.066399999/log10(M_E) */ quant_table[i] = expf(i * 0.15289164787221953823f) * root; } /* calculate number of bands */ //bands calculation: //bands[0] = 1; //foreach (i in 1..# of bands-1): //bands[i] = crit_freq[i-1] * (frame length / 2) / (sample rate / 2); //bands[# of bands] = frame length / 2 for (num_bands = 1; num_bands < 25; num_bands++) if (sample_rate_half <= ff_wma_critical_freqs[num_bands - 1]) break; bands = (unsigned int *)(av_malloc((num_bands + 1) * sizeof(*bands))); if (!bands) return AVERROR(ENOMEM); /* populate bands data */ bands[0] = 2; for (i = 1; i < num_bands; i++) bands[i] = (ff_wma_critical_freqs[i - 1] * frame_len / sample_rate_half) & ~1; bands[num_bands] = frame_len; first = 1; //ff_rdft_init(&trans.rdft, frame_len_bits, DFT_C2R); avcodec_get_frame_defaults(&frame); dec_ctx->coded_frame = &frame; } break; /* case AV_CODEC_ID_SMACKAUDIO: { static int smack_debug_next_audio_time = 0; if (smack_debug_next_audio_time != packet->pts) { Error("There's a gap in the sound before frame %u\n", num_audio_frames); __debugbreak(); // there's a gap in the sound } int num_actual_data_channels = 0; switch (dec_ctx->sample_fmt) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_DBL: num_actual_data_channels = 1; break; case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: case AV_SAMPLE_FMT_DBLP: num_actual_data_channels = dec_ctx->channels; break; default: case AV_SAMPLE_FMT_NONE: case AV_SAMPLE_FMT_NB: __debugbreak(); } smack_debug_next_audio_time += dec_ctx->channels * frame->nb_samples * bytes_per_sample; Assert(frame->avframe->linesize[0] == audio.dec_ctx->channels * frame->avframe->nb_samples * audio.bytes_per_sample / num_actual_data_channels, "Smack audio size mismatch in frame %u in %s\n", audio_num_read_frames, movie_filename); frame->play_time = (double)frame->avpacket->pts / (double)audio.bytes_per_second; } break; case AV_CODEC_ID_MP3: { static int mp3_samples_decoded_so_far = 0; static int mp3_prev_samples_count = frame->avframe->nb_samples; // mp3 seems to always feed same amount of samples frame->play_time = (double)mp3_samples_decoded_so_far / (double)audio.dec_ctx->sample_rate; mp3_samples_decoded_so_far += frame->avframe->nb_samples; Assert(mp3_prev_samples_count == frame->avframe->nb_samples, "MP3 audio have variable sample count in frame %u in %s\n", audio_num_read_frames, movie_filename); } break; default: { __debugbreak(); double samples_per_second = (double)audio.dec_ctx->time_base.den / (double)audio.dec_ctx->time_base.num; double play_length = frame->avframe->nb_samples / samples_per_second; frame->play_time = (double)frame->avpacket->pts / samples_per_second; } break;*/ } if (!avframe->channel_layout) { log("Audio channel layout not specified, rolling back to default\n"); avframe->channel_layout = av_get_default_channel_layout(dec_ctx->channels); } *out_num_audio_samples = dec_ctx->channels * avframe->nb_samples; InterleaveAudioData(out_audio_data, dec_ctx->sample_fmt, dec_ctx->channels, avframe->nb_samples, avframe->data); return true; } bool LoadAudioTrack(AVFormatContext *format_ctx, AVCodecContext *dec_ctx, int audio_stream_idx, MemoryStream *out_audio_stream, int *out_num_audio_frames, int *out_num_audio_samples) { out_audio_stream->Reset(); AVFrame *frame = avcodec_alloc_frame(); AVPacket *packet = new AVPacket; av_init_packet(packet); int num_audio_frames = 0; int num_audio_samples = 0; while (av_read_frame(format_ctx, packet) >= 0) { // Is this a packet from the audio stream? if (packet->stream_index != audio_stream_idx) { //log("Suspicious stream id %u in %s", packet->stream_index, filenamea); continue; } // Decode audio frame int num_samples_decoded; DecodeAudioFrame(dec_ctx, packet, frame, out_audio_stream, &num_samples_decoded); num_audio_samples += num_samples_decoded; num_audio_frames++; } *out_num_audio_frames = num_audio_frames; *out_num_audio_samples = num_audio_samples; avcodec_free_frame(&frame); delete frame; av_free_packet(packet); delete packet; return true; } class Track: public Media::ITrack { public: inline Track() { this->format_ctx = nullptr; this->audio_num_samples = 0; } void Release() { ReleaseAvcodec(); } void ReleaseAvcodec() { audio.Release(); if (format_ctx) { av_close_input_file(format_ctx); Log::Warning(L"close audio format context file\n"); format_ctx = nullptr; } } bool LoadAudio(const wchar_t *filename) { char filenamea[1024]; sprintf(filenamea, "%S", filename); // Open audio file if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) { // Retrieve stream information if (avformat_find_stream_info(format_ctx, nullptr) >= 0) { // Dump information about file onto standard error av_dump_format(format_ctx, 0, filenamea, 0); if (!av_open_audio_stream(format_ctx, &audio)) { Error("Cannot open strack: %s", filenamea); return Release(), false; } MemoryStream audio_plain_data; int num_audio_frames; int num_audio_samples; if (LoadAudioTrack(format_ctx, audio.dec_ctx, audio.stream_idx, &audio_plain_data, &num_audio_frames, &num_audio_samples)) { /*#ifdef _DEBUG char debug_filename[1024]; sprintf(debug_filename, "%s.wav", filenamea); FILE *wav = fopen(debug_filename, "w+b"); extern void write_wav_header(FILE *wav, int channel_count = 2, int sample_rate = 22050, int bytes_per_sample = 2); write_wav_header(wav, audio.dec_ctx->channels, audio.dec_ctx->sample_rate, audio.bytes_per_sample); fwrite(audio_plain_data.Ptr(), audio_plain_data.Current(), 1, wav); extern void fix_wav_header(FILE *wav, int wav_bytes_in_stream); fix_wav_header(wav, audio_plain_data.Current()); #endif*/ device_buffer = provider->CreateTrack16(audio.dec_ctx->channels, audio.dec_ctx->sample_rate, 2, num_audio_samples, audio_plain_data.Ptr()); Release(); return true; } } Release(); fprintf(stderr, "ffmpeg: Unable to find stream info\n"); return false; } fprintf(stderr, "ffmpeg: Unable to open input file\n"); return false; } virtual void Play(bool loop) { provider->PlayTrack16(device_buffer, loop); mSourceID = device_buffer->source_id; } protected: AVFormatContext *format_ctx; AVAudioStream audio; int audio_num_samples; bool stopped; OpenALSoundProvider::TrackBuffer *device_buffer; }; class Movie: public Media::IMovie { public: inline Movie() { this->movie_filename[0] = 0; this->width = 0; this->height = 0; this->format_ctx = nullptr; this->end_of_file = false; this->playback_time = 0.0; this->num_audio_frames = 0; this->num_audio_samples = 0; this->last_resampled_frame_num = -1; memset(last_resampled_frame_data, 0, sizeof(last_resampled_frame_data)); memset(last_resampled_frame_linesize, 0, sizeof(last_resampled_frame_linesize)); audio_data_in_device = nullptr; decoding_packet = nullptr; ioBuffer = nullptr; format_ctx = nullptr; avioContext = nullptr; } virtual ~Movie() {} virtual void Release() { ReleaseAVCodec(); if (audio_data_in_device) provider->DeleteStreamingTrack(&audio_data_in_device); } inline void ReleaseAVCodec() { audio.Release(); video.Release(); if (format_ctx) { // Close the video file av_close_input_file(format_ctx); Log::Warning(L"close video format context file\n"); format_ctx = nullptr; } if(avioContext) { av_free(avioContext); avioContext = nullptr; } if (ioBuffer) { //av_free(ioBuffer); ioBuffer = nullptr; } av_free_packet(decoding_packet); delete decoding_packet; avcodec_free_frame(&decoding_frame); delete decoding_frame; if (last_resampled_frame_data[0]) av_freep(&last_resampled_frame_data[0]); } bool Load(const wchar_t *filename, int dst_width, int dst_height, int cache_ms) //Загрузка { char filenamea[1024]; sprintf(filenamea, "%S", filename); sprintf(movie_filename, "%S", filename); width = dst_width; height = dst_height; // Open video file if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) { // Retrieve stream information if (avformat_find_stream_info(format_ctx, nullptr) >= 0) { // Dump information about file onto standard error av_dump_format(format_ctx, 0, filenamea, 0); /*if (!av_open_audio_stream(format_ctx, &audio)) { Error("Cannot open audio stream: %s", filenamea); return Release(), false; }*/ av_open_audio_stream(format_ctx, &audio); if (!av_open_video_stream(format_ctx, &video)) { Error("Cannot open video stream: %s", filenamea); return Release(), false; } //Ritor1: include if (_stricmp("binkvideo", video.dec->name) ) { pMediaPlayer->current_movie_width = video.dec_ctx->width; pMediaPlayer->current_movie_height = video.dec_ctx->height; } else { pMediaPlayer->current_movie_width = width; pMediaPlayer->current_movie_height = height; } // decoding_packet = new AVPacket; av_init_packet(decoding_packet); // Allocate video frame decoding_frame = avcodec_alloc_frame(); if (audio.stream_idx >= 0) audio_data_in_device = provider->CreateStreamingTrack16(audio.dec_ctx->channels, audio.dec_ctx->sample_rate, 2); return true; } fprintf(stderr, "ffmpeg: Unable to find stream info\n"); return Release(), false; } fprintf(stderr, "ffmpeg: Unable to open input file\n"); return Release(), false; } bool LoadFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height) { if (!ioBuffer) ioBuffer = (unsigned char *)av_malloc(0x4000 + FF_INPUT_BUFFER_PADDING_SIZE); // can get av_free()ed by libav if (!avioContext) avioContext = avio_alloc_context(ioBuffer, 0x4000, 0, h, readFunction, NULL, seekFunction); if (!format_ctx) format_ctx = avformat_alloc_context(); format_ctx->pb = avioContext; return Load(L"dummyFilename", width, height, 0); } virtual void GetNextFrame(double dt, void *dst_surface) { playback_time += dt; AVPacket *avpacket = decoding_packet; AVFrame *avframe = decoding_frame; avcodec_get_frame_defaults(avframe); int desired_frame_number = floor(playback_time * video.dec_ctx->time_base.den / video.dec_ctx->time_base.num + 0.5); if (last_resampled_frame_num == desired_frame_number) { memcpy(dst_surface, last_resampled_frame_data[0], pMediaPlayer->current_movie_height * last_resampled_frame_linesize[0]); return; } volatile int frameFinished = false; // keep reading packets until we hit the end or find a video packet do { if (pMediaPlayer->loop_current_file) { //Now seek back to the beginning of the stream if (video.dec_ctx->frame_number >= video.stream->duration - 1 ) pMediaPlayer->bPlaying_Movie = false; } if (av_read_frame(format_ctx, avpacket) < 0) { // probably movie is finished pMediaPlayer->bPlaying_Movie = false; av_free_packet(avpacket); return; } // Is this a packet from the video stream? // audio packet - queue into playing if (avpacket->stream_index == audio.stream_idx) { MemoryStream audio_data; if (DecodeAudioFrame(audio.dec_ctx, avpacket, avframe, &audio_data, &num_audio_samples)) provider->Stream16(audio_data_in_device, num_audio_samples, audio_data.Ptr()); //continue; } // Decode video frame // video packet - decode & maybe show else if (avpacket->stream_index == video.stream_idx) { do { if (avcodec_decode_video2(video.dec_ctx, avframe, (int *)&frameFinished, avpacket) < 0) __debugbreak(); } while (!frameFinished); } else __debugbreak(); // unknown stream } while (avpacket->stream_index != video.stream_idx || avpacket->pts != desired_frame_number); if (frameFinished) { if (last_resampled_frame_data[0]) av_freep(&last_resampled_frame_data[0]); AVPixelFormat rescaled_format = AV_PIX_FMT_RGB32; uint8_t *rescaled_data[4] = {nullptr, nullptr, nullptr, nullptr}; int rescaled_linesize[4] = {0, 0, 0, 0}; if (av_image_alloc(rescaled_data, rescaled_linesize, pMediaPlayer->current_movie_width, pMediaPlayer->current_movie_height, rescaled_format, 1) >= 0) { SwsContext *converter = sws_getContext(avframe->width, avframe->height, (AVPixelFormat)avframe->format, pMediaPlayer->current_movie_width, pMediaPlayer->current_movie_height, rescaled_format, SWS_BICUBIC, nullptr, nullptr, nullptr); sws_scale(converter, avframe->data, avframe->linesize, 0, avframe->height, rescaled_data, rescaled_linesize); sws_freeContext(converter); memcpy(dst_surface, rescaled_data[0], pMediaPlayer->current_movie_height * rescaled_linesize[0]); last_resampled_frame_num = desired_frame_number; memcpy(last_resampled_frame_data, rescaled_data, sizeof(rescaled_data)); memcpy(last_resampled_frame_linesize, rescaled_linesize, sizeof(rescaled_linesize)); } } else memset(dst_surface, 0, width * pMediaPlayer->current_movie_height * 4); // Free the packet that was allocated by av_read_frame av_free_packet(avpacket); } virtual void Play() { } protected: char movie_filename[256]; int width; int height; bool stopped; AVFormatContext *format_ctx; double playback_time; bool end_of_file; AVPacket *decoding_packet; AVFrame *decoding_frame; AVAudioStream audio; int num_audio_frames; int num_audio_samples; unsigned char *ioBuffer; AVIOContext *avioContext; OpenALSoundProvider::StreamingTrackBuffer *audio_data_in_device; AVVideoStream video; int last_resampled_frame_num; uint8_t *last_resampled_frame_data[4]; int last_resampled_frame_linesize[4]; }; ITrack *MPlayer::LoadTrack(const wchar_t *filename) { auto audio_track = new Track; Log::Warning(L"allocation dynamic memory for audio_track\n"); if (!audio_track->LoadAudio(filename)) { delete audio_track; Log::Warning(L"delete dynamic memory for audio_track\n"); audio_track = nullptr; } return audio_track; } IMovie *MPlayer::LoadMovie(const wchar_t *filename, int width, int height, int cache_ms) //Загрузить видео { movie = new Movie; Log::Warning(L"allocation dynamic memory for movie\n"); if (!movie->Load(filename, width, height, cache_ms)) { delete movie; Log::Warning(L"delete dynamic memory for movie\n"); movie = nullptr; } return movie; } //for video///////////////////////////////////////////////////////////////// //----- (004BE9D8) -------------------------------------------------------- void MPlayer::Initialize(OSWindow *target_window) { DWORD NumberOfBytesRead; // [sp+10h] [bp-4h]@9 window = target_window; unsigned int uBinkVersionMajor = -1, uBinkVersionMinor = -1; //GetDllVersion(L"BINKW32.DLL", &uBinkVersionMajor, &uBinkVersionMinor); //uBinkVersion = (unsigned __int64)uBinkVersionMajor << 32 | uBinkVersionMinor; strcpy(pTmpBuf.data(), "anims\\might7.vid"); hMightVid = CreateFileW(L"anims\\might7.vid", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0x8000080, 0); if ( hMightVid == INVALID_HANDLE_VALUE ) { sprintf(pTmpBuf2.data(), "Can't open file - anims\\%s.smk", pTmpBuf.data()); MessageBoxA(0, pTmpBuf2.data(), "Video File Error", 0); return; } strcpy(pTmpBuf.data(), "anims\\magic7.vid"); hMagicVid = CreateFileW(L"anims\\magic7.vid", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0x8000080, 0); if ( hMagicVid == INVALID_HANDLE_VALUE ) { if ( !bCanLoadFromCD ) { sprintf(pTmpBuf2.data(), "Can't open file - anims\\%s.smk", pTmpBuf.data()); MessageBoxA(0, pTmpBuf2.data(), "Video File Error", 0); return; } sprintf(pTmpBuf2.data(), "%c:\\%s", (unsigned __int8)cMM7GameCDDriveLetter, pTmpBuf.data()); hMagicVid = CreateFileA(pTmpBuf2.data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0x8000080, 0); if ( hMagicVid == (HANDLE)INVALID_HANDLE_VALUE ) { sprintf(pTmpBuf2.data(), "Can't open file - %s", pTmpBuf.data()); MessageBoxA(0, pTmpBuf2.data(), "Video File Error", 0); return; } } ReadFile(hMightVid, &uNumMightVideoHeaders, 4, &NumberOfBytesRead, 0); ReadFile(hMagicVid, &uNumMagicVideoHeaders, 4, &NumberOfBytesRead, 0); pMightVideoHeaders = (MovieHeader *)malloc(sizeof(MovieHeader) * uNumMightVideoHeaders + 2); pMagicVideoHeaders = (MovieHeader *)malloc(sizeof(MovieHeader) * uNumMagicVideoHeaders + 2); ReadFile(hMightVid, pMightVideoHeaders, 44 * uNumMightVideoHeaders, &NumberOfBytesRead, 0); ReadFile(hMagicVid, pMagicVideoHeaders, 44 * uNumMagicVideoHeaders, &NumberOfBytesRead, 0); } //----- (004BF411) -------------------------------------------------------- void MPlayer::OpenFullscreenMovie(const char *pFilename, unsigned int bLoop/*, int ScreenSizeFlag*/) { if (!this->bPlaying_Movie) { pEventTimer->Pause(); if ( use_music_folder ) alSourcePause(mSourceID); else if (pAudioPlayer->hAILRedbook) AIL_redbook_pause(pAudioPlayer->hAILRedbook); bStopBeforeSchedule = false; bFirstFrame = false; this->bLoopPlaying = bLoop; LoadMovie(pFilename); return; } } //----- (004BF28F) -------------------------------------------------------- void MPlayer::OpenHouseMovie(const char *pMovieName, unsigned int a3_1) { if (!this->bPlaying_Movie) { //Prepare(); pEventTimer->Pause(); if ( use_music_folder ) alSourcePause(mSourceID); else if (pAudioPlayer->hAILRedbook) AIL_redbook_pause(pAudioPlayer->hAILRedbook); bStopBeforeSchedule = false; bFirstFrame = false; this->bLoopPlaying = a3_1; /*if ( LOBYTE(this->field_104) == 1 ) { MessageBoxA(nullptr, "Unsupported Bink playback!", "E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Video.cpp:925", 0); return; }*/ LoadMovie(pMovieName); time_video_begin = GetTickCount(); } } //----- (004BE70E) -------------------------------------------------------- void MPlayer::FullscreenMovieLoop(const char *pMovieName, int a2/*, int ScreenSizeFlag, int a4*/) { int v4; // ebp@1 MSG Msg; // [sp+Ch] [bp-1Ch]@12 v4 = a2; if ( dword_6BE364_game_settings_1 & (GAME_SETTINGS_NO_HOUSE_ANIM | GAME_SETTINGS_NO_INTRO) || bNoVideo) return; if ( a2 == 2 ) v4 = 0; ShowCursor(0); OpenFullscreenMovie(pMovieName, 0); bPlaying_Movie = 1; field_44 = v4; pRenderer->ClearTarget(0); pCurrentScreen = SCREEN_VIDEO; auto hwnd = pMediaPlayer->window->GetApiHandle(); RECT rc_client; GetClientRect(hwnd, &rc_client); int client_width = rc_client.right - rc_client.left, client_height = rc_client.bottom - rc_client.top; HDC dc = GetDC(hwnd); HDC back_dc = CreateCompatibleDC(dc); HBITMAP back_bmp = CreateCompatibleBitmap(dc, client_width, client_height); auto frame_buffer = new char[client_width * client_height * 4]; SelectObject(back_dc, back_bmp); DWORD t = GetTickCount(); bPlaying_Movie = true; while (true) { if (pMediaPlayer->bStopBeforeSchedule) break; while (PeekMessageA(&Msg, hwnd, 0, 0, PM_REMOVE)) { if (Msg.message == WM_QUIT) Game_DeinitializeAndTerminate(0); if (Msg.message == WM_PAINT) break; TranslateMessage(&Msg); DispatchMessageA(&Msg); } double dt = (GetTickCount() - t) / 1000.0; t = GetTickCount(); pMovie_Track->GetNextFrame(dt, frame_buffer); if (!bPlaying_Movie) break; if (frame_buffer) { // draw to hwnd BITMAPINFO bmi; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = client_width; bmi.bmiHeader.biHeight = -client_height; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = 0; bmi.bmiHeader.biXPelsPerMeter = 0; bmi.bmiHeader.biYPelsPerMeter = 0; bmi.bmiHeader.biClrUsed = 0; bmi.bmiHeader.biClrImportant = 0; GetDIBits(back_dc, back_bmp, 0, client_height, 0, &bmi, DIB_RGB_COLORS); SetDIBits(back_dc, back_bmp, 0, client_height, frame_buffer, &bmi, DIB_RGB_COLORS); BitBlt(dc, 0, 0, client_width, client_height, back_dc, 0, 0, SRCCOPY); } GUI_MainMenuMessageProc(); if (pMediaPlayer->bStopBeforeSchedule == 1) Sleep(1000); } delete [] frame_buffer; DeleteObject(back_bmp); DeleteObject(back_dc); ReleaseDC(hwnd, dc); pMediaPlayer->Unload(); //if (a4 == 1) pCurrentScreen = SCREEN_GAME; pMediaPlayer->bPlaying_Movie = false; ShowCursor(1); /*if ( pCurrentScreen == SCREEN_VIDEO ) pCurrentScreen = SCREEN_GAME;*/ } void MPlayer::HouseMovieLoop() { if (pMovie_Track && !bNoVideo) { pRenderer->BeginScene(); pMouse->DrawCursorToTarget(); Log::Warning(L"smacker"); loop_current_file = true; pRenderer->BeginScene(); if (!bPlaying_Movie)//reload { unsigned int width = game_viewport_width; unsigned int height = game_viewport_height; MovieRelease(); SetFilePointer(hVidFile, uOffset, nullptr, FILE_BEGIN); pMovie_Track = nullptr; Log::Warning(L"reload pMovie_Track"); pMovie_Track = pMediaPlayer->LoadMovieFromLOD(hVidFile, &readFunction, &seekFunction, width, height); bPlaying_Movie = true; } //else //{ double dt = (GetTickCount() - time_video_begin) / 1000.0; //dt = 1.0/15.0; time_video_begin = GetTickCount(); //log("dt=%.5f\n", dt); auto image = new char[current_movie_width * current_movie_height * 4]; pMovie_Track->GetNextFrame(dt, image); int image_array[460 * 344];//game_viewport_width * game_viewport_height if (image) { memcpy(image_array, image, sizeof (image_array)); for (unsigned int y = 8; y < 8 + game_viewport_height; ++y)//координаты местоположения видеоролика { for (unsigned int x = 8; x < 8 + game_viewport_width; ++x) { auto p = (unsigned __int32 *)pRenderer->pTargetSurface + x + y * pRenderer->uTargetSurfacePitch; *p = image_array[((x - 8) + ((y - 8)*game_viewport_width))]; } } delete[] image; } //} pRenderer->EndScene(); pMouse->ReadCursorWithItem(); pRenderer->EndScene(); } } //----- (004BF73A) -------------------------------------------------------- void MPlayer::SelectMovieType() { char Source[32]; // [sp+Ch] [bp-40h]@1 strcpy(Source, this->pCurrentMovieName); pMediaPlayer->Unload(); if ( this->uMovieType == 1 ) OpenHouseMovie(Source, LOBYTE(this->bLoopPlaying)); else if ( this->uMovieType == 2 ) OpenFullscreenMovie(Source, LOBYTE(this->bLoopPlaying)); else __debugbreak(); } void MPlayer::LoadMovie(const char *pFilename) { char pVideoNameBik[120]; // [sp+Ch] [bp-28h]@2 char pVideoNameSmk[120]; // [sp+Ch] [bp-28h]@2 sprintf(pVideoNameBik, "%s.bik", pFilename); sprintf(pVideoNameSmk, "%s.smk", pFilename); for (uint i = 0; i < uNumMightVideoHeaders; ++i) { if (!_stricmp(pVideoNameSmk, pMightVideoHeaders[i].pVideoName)) { hVidFile = hMightVid; uOffset = pMightVideoHeaders[i].uFileOffset; uSize = pMightVideoHeaders[i + 1].uFileOffset - uOffset; this->uMovieType = 2; } } for (uint i = 0; i < uNumMagicVideoHeaders; ++i) { if (!_stricmp(pVideoNameBik, pMagicVideoHeaders[i].pVideoName)) { hVidFile = hMagicVid; uOffset = pMagicVideoHeaders[i].uFileOffset; uSize = pMagicVideoHeaders[i + 1].uFileOffset - uOffset; this->uMovieType = 1; } if (!_stricmp(pVideoNameSmk, pMagicVideoHeaders[i].pVideoName)) { hVidFile = hMagicVid; uOffset = pMagicVideoHeaders[i].uFileOffset; uSize = pMagicVideoHeaders[i + 1].uFileOffset - uOffset; this->uMovieType = 2; } } if (!hVidFile) { pMediaPlayer->Unload(); MessageBoxA(0, "MediaPlayer error", "MediaPlayer Error", 0); return; } SetFilePointer(hVidFile, uOffset, 0, FILE_BEGIN); strcpy(this->pCurrentMovieName, pFilename); auto hwnd = pMediaPlayer->window->GetApiHandle(); RECT rc_client; GetClientRect(hwnd, &rc_client); int client_width = rc_client.right - rc_client.left, client_height = rc_client.bottom - rc_client.top; pMovie_Track = pMediaPlayer->LoadMovieFromLOD(hVidFile, &readFunction, &seekFunction, client_width, client_height); } //----- (004BF794) -------------------------------------------------------- void MPlayer::ShowMM7IntroVideo_and_LoadingScreen() { RGBTexture tex; // [sp+Ch] [bp-30h]@1 unsigned int uTrackStartMS; // [sp+34h] [bp-8h]@8 unsigned int uTrackEndMS; // [sp+38h] [bp-4h]@8 pMediaPlayer->bStopBeforeSchedule = false; // pMediaPlayer->pResetflag = 0; bGameoverLoop = true; if (!bNoVideo) { pRenderer->PresentBlackScreen(); if ( !pMediaPlayer->bStopBeforeSchedule ) PlayFullscreenMovie(MOVIE_Intro, true); } tex.Load("mm6title.pcx", 2); pRenderer->BeginScene(); pRenderer->DrawTextureRGB(0, 0, &tex); free(tex.pPixels); tex.pPixels = 0; //LoadFonts_and_DrawCopyrightWindow(); DrawMM7CopyrightWindow(); pRenderer->EndScene(); pRenderer->Present(); #ifndef _DEBUG Sleep(1500); // let the copyright window stay for a while #endif if (!bNoSound ) { if ( use_music_folder ) { PlayAudio(L"Music\\14.mp3"); alSourcef (mSourceID, AL_GAIN, pSoundVolumeLevels[uMusicVolimeMultiplier]); } else if ( pAudioPlayer->hAILRedbook ) { pAudioPlayer->SetMusicVolume((signed __int64)(pSoundVolumeLevels[uMusicVolimeMultiplier] * 64.0)); AIL_redbook_stop(pAudioPlayer->hAILRedbook); AIL_redbook_track_info(pAudioPlayer->hAILRedbook, 14, &uTrackStartMS, &uTrackEndMS); AIL_redbook_play(pAudioPlayer->hAILRedbook, uTrackStartMS + 1, uTrackEndMS); } } bGameoverLoop = false; } //----- (004BEBD7) -------------------------------------------------------- void MPlayer::Unload() { bPlaying_Movie = false; uMovieType = 0; memset(pCurrentMovieName, 0, 0x40); if ( !bGameoverLoop && pMediaPlayer->current_movie_width == 460) { if ( use_music_folder ) alSourcePlay(mSourceID); else if ( pAudioPlayer->hAILRedbook ) AIL_redbook_resume(pAudioPlayer->hAILRedbook); } pEventTimer->Resume(); pMovie_Track->Release(); delete pMovie_Track; pMovie_Track = nullptr; } int MPlayer::readFunction(void* opaque, uint8_t* buf, int buf_size) { HANDLE stream = (HANDLE)opaque; //int numBytes = stream->read((char*)buf, buf_size); int numBytes; ReadFile(stream, (char *)buf, buf_size, (LPDWORD)&numBytes, NULL); return numBytes; } int64_t MPlayer::seekFunction(void* opaque, int64_t offset, int whence) { if (whence == AVSEEK_SIZE) return pMediaPlayer->uSize; HANDLE h = (HANDLE)opaque; LARGE_INTEGER li; li.QuadPart = offset; if (!SetFilePointerEx(h, li, (PLARGE_INTEGER)&li, FILE_BEGIN)) return -1; return li.QuadPart; } //for video////////////////////////////////////////////////////////////////// IMovie *MPlayer::LoadMovieFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height) { movie = new Movie; Log::Warning(L"allocation dynamic memory for movie\n"); if (movie) { if (movie->LoadFromLOD(h, readFunction, seekFunction, width, height)) return movie; delete movie; Log::Warning(L"delete dynamic memory for movie\n"); } return nullptr; } void MovieRelease() { movie->Release(); delete movie; Log::Warning(L"delete dynamic memory for movie\n"); movie = nullptr; } //for audio/////////////////////////////////////////////////////// //----- (004AB818) -------------------------------------------------------- void MPlayer::LoadAudioSnd() { DWORD NumberOfBytesRead; // [sp+Ch] [bp-4h]@3 hAudioSnd = CreateFileA("Sounds\\Audio.snd", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0x8000080u, 0); if (hAudioSnd == INVALID_HANDLE_VALUE) { Log::Warning(L"Can't open file: %s", L"Sounds\\Audio.snd"); return; } ReadFile(hAudioSnd, &uNumSoundHeaders, 4, &NumberOfBytesRead, 0); pSoundHeaders = nullptr; pSoundHeaders = (SoundHeader *)malloc(52 * uNumSoundHeaders + 2); ReadFile(hAudioSnd, pSoundHeaders, 52 * uNumSoundHeaders, &NumberOfBytesRead, 0); } //for audio/////////////////////////////////////////////////////// void av_logger(void *, int, const char *format, va_list args) { va_list va; va_start(va, format); char msg[256]; vsprintf(msg, format, va); va_end(va); log("av: %s", msg); } MPlayer::MPlayer() { bPlaying_Movie = false; static int libavcodec_initialized = false; if (!libavcodec_initialized) { av_log_set_callback(av_logger); avcodec_register_all(); // Register all available file formats and codecs av_register_all(); libavcodec_initialized = true; } bStopBeforeSchedule = false; pMovie_Track = nullptr; if (!provider) { provider = new OpenALSoundProvider; Log::Warning(L"allocation dynamic memory for provider\n"); provider->Initialize(); } LoadAudioSnd(); } MPlayer::~MPlayer() { delete provider; Log::Warning(L"delete dynamic memory for provider\n"); bStopBeforeSchedule = false; // pResetflag = 0; pVideoFrame.Release(); } void PlayAudio(const wchar_t * pFilename) { pAudio_Track = pMediaPlayer->LoadTrack(pFilename); pAudio_Track->Play(); delete pAudio_Track; Log::Warning(L"delete dynamic memory for pAudio_Track\n"); pAudio_Track = nullptr; } void PlayMovie(const wchar_t * pFilename) { Media::IMovie *Movie_track = pMediaPlayer->LoadMovie(pFilename, 640, 480, 0); Movie_track->Play(); }