Mercurial > mm7
changeset 2345:182effc4b0ee
for MultimediaPlayer
author | Ritor1 |
---|---|
date | Mon, 07 Apr 2014 19:15:13 +0600 |
parents | d6887ee81068 |
children | 37bb43874e59 |
files | Game.cpp MMT.cpp MediaPlayer.cpp MediaPlayer.h UI/UIHouses.cpp VideoPlayer.cpp VideoPlayer.h |
diffstat | 7 files changed, 427 insertions(+), 94 deletions(-) [+] |
line wrap: on
line diff
--- a/Game.cpp Wed Apr 02 23:21:34 2014 +0200 +++ b/Game.cpp Mon Apr 07 19:15:13 2014 +0600 @@ -4486,7 +4486,7 @@ pVideoPlayer->bStopBeforeSchedule = 1; viewparams->bRedrawGameUI = 1; viewparams->field_48 = 1; - if ( !GetCurrentMenuID() || GetCurrentMenuID() == MENU_CREATEPARTY || GetCurrentMenuID() == MENU_NAMEPANELESC ) + if ( !GetCurrentMenuID() || GetCurrentMenuID() == MENU_MMT_MAIN_MENU || GetCurrentMenuID() == MENU_CREATEPARTY || GetCurrentMenuID() == MENU_NAMEPANELESC ) { //if ( pCurrentScreen == SCREEN_VIDEO ) //pVideoPlayer->FastForwardToFrame(pVideoPlayer->pResetflag);
--- a/MMT.cpp Wed Apr 02 23:21:34 2014 +0200 +++ b/MMT.cpp Mon Apr 07 19:15:13 2014 +0600 @@ -184,17 +184,9 @@ pAudioPlayer->StopChannels(-1, -1);//остановить/подготовить канал if (!bNoSound ) - { - Media::Player *p = new Media::Player;//создаётся плеер - Media::ITrack *track = p->LoadTrack(L"Sounds\\New_Sounds/Stronghold_Theme.mp3"); - track->Play(); - } - /*if (!bNoVideo ) - { - Media::Player *p = new Media::Player;//создаётся плеер - Media::IMovie *track = p->LoadMovie(L"Anims\\New_Video/3DOLOGO.smk", 640, 480, 0); - track->Play(); - } */ + pVideoPlayer->PlayAudio(L"Sounds\\New_Sounds/Stronghold_Theme.mp3");//воспроизводим мп3 + //if (!bNoVideo ) + //pVideoPlayer->PlayMovie(L"Anims\\New_Video/3DOLOGO.smk"); pMouse->RemoveHoldingItem();//избавить курсор от вещи
--- a/MediaPlayer.cpp Wed Apr 02 23:21:34 2014 +0200 +++ b/MediaPlayer.cpp Mon Apr 07 19:15:13 2014 +0600 @@ -119,7 +119,11 @@ }; - +bool end_current_file; +bool loop_current_file; +DWORD time_video_begin; +int current_movie_width; +int current_movie_height; OpenALSoundProvider *provider = nullptr; @@ -175,6 +179,7 @@ dec = nullptr; if (dec_ctx) { + // закрытие видео кодека avcodec_close(dec_ctx); dec_ctx = nullptr; } @@ -213,14 +218,19 @@ 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]; +//Информация о кодеке в потоке называется «контекстом кодека» (AVCodecContext). +//Используя эту информацию, мы можем найти необходимый кодек (AVCodec) и открыть его. auto dec_ctx = stream->codec; + // ищем декодер auto dec = avcodec_find_decoder(dec_ctx->codec_id); if (dec) { + // открываем декодер if (avcodec_open2(dec_ctx, dec, nullptr) >= 0) { out_stream->type = type; @@ -231,6 +241,7 @@ return true; } } + fprintf(stderr, "ffmpeg: Unable to open codec\n"); } return false; } @@ -241,11 +252,11 @@ 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; - } + //if (out_stream->dec_ctx->channels != 2)//закомментировал потому что при воспроизведении jvc.bik вылетает на этом месте + //{ + // 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; @@ -339,6 +350,7 @@ volatile int decoded = false; do { +//Декодирование аудио-пакета осуществляется функцией avcodec_decode_audio4 if (avcodec_decode_audio4(dec_ctx, avframe, (int *)&decoded, avpacket) < 0) { log("Cannot decode audio frame\n"); @@ -462,14 +474,18 @@ { out_audio_stream->Reset(); +//воспроизведение audio. +//Данные из файла читаются пакетами (AVPacket), а для воспроизведения используется фрейм (AVFrame). 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) { + //Принадлежит ли пакет к аудиопотоку if (packet->stream_index != audio_stream_idx) { //log("Suspicious stream id %u in %s", packet->stream_index, filenamea); @@ -511,6 +527,7 @@ audio.Release(); if (format_ctx) { + // закрытия контекста файла av_close_input_file(format_ctx); format_ctx = nullptr; } @@ -520,11 +537,22 @@ { char filenamea[1024]; sprintf(filenamea, "%S", filename); - +// Open audio file +//откроем входной файл(Шаг 2) +//Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре +//AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует +//автоматическое определение параметров. if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) { +// Retrieve stream information +//Т.к. avformat_open_input читает только заголовок файла, то следующим шагом нужно получить информацию о потоках +//в файле. Это делается функцией avformat_find_stream_info. if (avformat_find_stream_info(format_ctx, nullptr) >= 0) { +// Dump information about file onto standard error +//После этого format_context->streams содержит все существующие потоки файла. +//Их количество равно format_context->nb_streams. +//Вывести подробную информацию о файле и обо всех потоках можно функцией av_dump_format. av_dump_format(format_ctx, 0, filenamea, 0); if (!av_open_audio_stream(format_ctx, &audio)) @@ -559,7 +587,10 @@ } } Release(); + fprintf(stderr, "ffmpeg: Unable to find stream info\n"); + return false; } + fprintf(stderr, "ffmpeg: Unable to open input file\n"); return false; } @@ -574,6 +605,8 @@ AVAudioStream audio; int audio_num_samples; + bool stopped; + OpenALSoundProvider::TrackBuffer *device_buffer; }; @@ -582,6 +615,18 @@ class Movie: public Media::IMovie { public: + int audio_stream_idx; + AVStream *audio_stream;//содержат информацию о аудио потоке + AVCodec *audio_stream_dec; + AVCodecContext *audio_stream_dec_ctx; + + int video_stream_idx; + AVStream *video_stream;//содержат информацию о видео потоке + AVCodec *video_stream_dec; + AVCodecContext *video_stream_dec_ctx; + bool Stopped() { return stopped; } + bool End_file() { return end_of_file; } + inline Movie() { this->movie_filename[0] = 0; @@ -597,6 +642,11 @@ 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)); + + decoding_packet = nullptr; + ioBuffer = nullptr; + format_ctx = nullptr; + avioContext = nullptr; } @@ -611,6 +661,7 @@ video.Release(); if (format_ctx) { + // закрытия контекста файла av_close_input_file(format_ctx); format_ctx = nullptr; } @@ -624,10 +675,22 @@ width = dst_width; height = dst_height; +// Open video file +//откроем входной файл(Шаг 2) +//Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре +//AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует +//автоматическое определение параметров. if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) { +// Retrieve stream information +//Т.к. avformat_open_input читает только заголовок файла, то следующим шагом нужно получить информацию о потоках +//в файле. Это делается функцией avformat_find_stream_info.(Шаг 3) if (avformat_find_stream_info(format_ctx, nullptr) >= 0) { +// Dump information about file onto standard error +//После этого format_context->streams содержит все существующие потоки файла. +//Их количество равно format_context->nb_streams. +//Вывести подробную информацию о файле и обо всех потоках можно функцией av_dump_format. av_dump_format(format_ctx, 0, filenamea, 0); if (!av_open_audio_stream(format_ctx, &audio)) @@ -642,6 +705,17 @@ return Release(), false; } + if (_stricmp("binkvideo", video.dec->name) ) + { + current_movie_width = video.dec_ctx->width; + current_movie_height = video.dec_ctx->height; + } + else + { + current_movie_width = width; + current_movie_height = height; + } + decoding_packet = new AVPacket; av_init_packet(decoding_packet); @@ -651,9 +725,95 @@ return true; } + fprintf(stderr, "ffmpeg: Unable to find stream info\n"); + return false; } + fprintf(stderr, "ffmpeg: Unable to open input file\n"); return 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(16384 + FF_INPUT_BUFFER_PADDING_SIZE); // can get av_free()ed by libav + if (!avioContext) + avioContext = avio_alloc_context(ioBuffer, 16384, 0, h, readFunction, NULL, seekFunction); + if (!format_ctx) + format_ctx = avformat_alloc_context(); + format_ctx->pb = avioContext; + return Load(L"dummyFilename", width, height, 0); + } + + bool Load(const char *video_filename, int width, int height) + { + this->width = width; + this->height = height; +// Open video file +//откроем входной файл(Шаг 2) +//Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре +//AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует +//автоматическое определение параметров. + if (avformat_open_input(&format_ctx, video_filename, nullptr, nullptr) >= 0) + { +// Retrieve stream information +//Т.к. avformat_open_input читает только заголовок файла, то следующим шагом нужно получить информацию о потоках +//в файле. Это делается функцией avformat_find_stream_info.(Шаг 3) + if (avformat_find_stream_info(format_ctx, nullptr) >= 0) + { +// Dump information about file onto standard error +//После этого format_context->streams содержит все существующие потоки файла. +//Их количество равно format_context->nb_streams. +//Вывести подробную информацию о файле и обо всех потоках можно функцией av_dump_format. + av_dump_format(format_ctx, 0, video_filename, 0); + + video_stream_idx = OpenStream(AVMEDIA_TYPE_VIDEO, &video_stream, &video_stream_dec, &video_stream_dec_ctx); + if (video_stream_idx < 0) + return Release(), false; + if (_stricmp("binkvideo", video_stream_dec->name) )//Ritor1: include + { + this->width = video_stream_dec_ctx->width; + this->height = video_stream_dec_ctx->height; + } + + audio_stream_idx = OpenStream(AVMEDIA_TYPE_AUDIO, &audio_stream, &audio_stream_dec, &audio_stream_dec_ctx); + if (audio_stream_idx < 0) + return Release(), false; + + strcpy(movie_filename, video_filename); +//Данные из файла читаются пакетами (AVPacket) + decoding_packet = new AVPacket; + av_init_packet(decoding_packet); + 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; + } + int OpenStream(AVMediaType type, AVStream **out_stream, AVCodec **out_dec, AVCodecContext **out_dec_ctx) + { + int stream_idx = av_find_best_stream(format_ctx, type, -1, -1, nullptr, 0); + if (stream_idx < 0) + return stream_idx; + + auto stream = format_ctx->streams[stream_idx]; +//Информация о кодеке в потоке называется «контекстом кодека» (AVCodecContext). +//Используя эту информацию, мы можем найти необходимый кодек (AVCodec) и открыть его. + auto dec_ctx = stream->codec; + auto dec = avcodec_find_decoder(dec_ctx->codec_id); + if (dec) + { + if (avcodec_open2(dec_ctx, dec, nullptr) >= 0) + { + *out_stream = stream; + *out_dec = dec; + *out_dec_ctx = dec_ctx; + return stream_idx; + } + } + fprintf(stderr, "ffmpeg: Unable to open codec\n"); + return -1; + } virtual void Play() { @@ -663,8 +823,11 @@ { playback_time += dt;//изменение времени +//Данные из файла читаются пакетами (AVPacket), а для отображения используется фрейм (AVFrame). +//(помните мы сохранили номер видео потока в переменной video_stream). 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); @@ -675,65 +838,134 @@ } volatile int decoded = false; - do + //чтение пакетов + // keep reading packets until we hit the end or find a video packet + while (av_read_frame(format_ctx, avpacket) >= 0) { - if (av_read_frame(format_ctx, avpacket) < 0) //воспроизведение завершено + /*if (av_read_frame(format_ctx, avpacket) < 0) //воспроизведение завершено { + if (loop_current_file) + { + av_seek_frame(format_ctx, video.stream_idx, 0, AVSEEK_FLAG_ANY); + } + else + { // probably movie is finished - __debugbreak(); - } + //__debugbreak(); + end_of_file = true; + end_current_file = true; + return;// nullptr; + } + } */ + // 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; } - // video packet - decode & maybe show + // Decode video frame + //пакет к видеопотоку else if (avpacket->stream_index == video.stream_idx) { - do +//Функция avcodec_decode_video2 осуществляет декодирование пакета в фрейм с использованием кодека, +//который мы получили раньше (codec_context). Функция устанавливает положительное значение frame_finished в случае +//если фрейм декодирован целиком (то есть один фрейм может занимать несколько пакетов и frame_finished будет +//установлен только при декодировании последнего пакета). + avcodec_decode_video2(video.dec_ctx, avframe, (int *)&decoded, avpacket); + if (decoded) { - if (avcodec_decode_video2(video.dec_ctx, avframe, (int *)&decoded, avpacket) < 0) - __debugbreak(); - } while (!decoded); - } - - } while (avpacket->stream_index != video.stream_idx || - avpacket->pts != desired_frame_number); - - if (decoded) - { - if (last_resampled_frame_data[0]) - av_freep(&last_resampled_frame_data[0]); + 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, width, height, rescaled_format, 1) >= 0) - { - SwsContext *converter = sws_getContext(avframe->width, avframe->height, (AVPixelFormat)avframe->format, - width, height, rescaled_format, + 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, current_movie_width, current_movie_height, rescaled_format, 1) >= 0) + { + // создание контекста для преобразования + SwsContext *converter = sws_getContext(avframe->width, avframe->height, (AVPixelFormat)avframe->format, + current_movie_width, 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); + // преобразование кадра + sws_scale(converter, avframe->data, avframe->linesize, 0, avframe->height, rescaled_data, rescaled_linesize); + sws_freeContext(converter); - memcpy(dst_surface, rescaled_data[0], height * rescaled_linesize[0]); + memcpy(dst_surface, rescaled_data[0], 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)); + 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, current_movie_width * current_movie_height * 4); } + return; + }// while (avpacket->stream_index != video.stream_idx || + // avpacket->pts != desired_frame_number); + if (loop_current_file) + { + av_seek_frame(format_ctx, video.stream_idx, 0, AVSEEK_FLAG_ANY); } else - memset(dst_surface, 0, width * height * 4); + { + // probably movie is finished + //__debugbreak(); + end_of_file = true; + end_current_file = true; + return;// nullptr; + } } + /*inline char *DoFrame() + { + if (!current_movie) + return nullptr; + + while (true) + { + auto frame = current_movie->GetNextFrame(); + if (!frame) + return nullptr; + + if (frame->Type() == AVMEDIA_TYPE_AUDIO) + { + //continue; +// uint8_t *data; + if (frame->Decode() >= 0) + { + auto f = frame->GetAVFrame(); + provider->PlaySample(f->channels, f->sample_rate, f->nb_samples, frame->GetData()); + Sleep(20); + continue; + } + } + else if (frame->Type() == AVMEDIA_TYPE_VIDEO) + { + uint8_t *dst_data[4] = { 0 }; + int dst_linesize[4] = { 0 }; + if (frame->Decode() >= 0) + { + auto image = new char[current_movie_width * current_movie_height * 4]; + memcpy(image, frame->GetData(), current_movie_height * frame->GetDataPitch()); + + return image; + } + } + return nullptr; + } + }*/ + protected: char movie_filename[256]; int width; int height; + bool stopped; AVFormatContext *format_ctx; double playback_time; bool end_of_file; @@ -744,6 +976,8 @@ AVAudioStream audio; int num_audio_frames; int num_audio_samples; + unsigned char * ioBuffer; + AVIOContext *avioContext; OpenALSoundProvider::StreamingTrackBuffer *audio_data_in_device; AVVideoStream video; @@ -800,6 +1034,11 @@ { av_log_set_callback(av_logger); avcodec_register_all(); + +// Register all available file formats and codecs +//инициализируем библиотеку ffmpeg(Шаг 1) +//Во время инициализации регистрируются все имеющиеся в библиотеке форматы файлов и кодеков. +//После этого они будут использоваться автоматически при открытии файлов этого формата и с этими кодеками. av_register_all(); libavcodec_initialized = true; @@ -814,4 +1053,27 @@ Player::~Player() { -} \ No newline at end of file +} + IMovie *Player::LoadMovieFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height) + { + auto movie = new Movie; + if (movie) + { + if (movie->LoadFromLOD(h, readFunction, seekFunction, width, height)) + { + /*if (_stricmp("binkvideo", movie->video_stream_dec->name) ) + { + current_movie_width = movie->video_stream_dec_ctx->width; + current_movie_height = movie->video_stream_dec_ctx->height; + } + else + { + current_movie_width = width; + current_movie_height = height; + } */ + return movie; + } + delete movie; + } + return nullptr; + } \ No newline at end of file
--- a/MediaPlayer.h Wed Apr 02 23:21:34 2014 +0200 +++ b/MediaPlayer.h Mon Apr 07 19:15:13 2014 +0600 @@ -11,6 +11,8 @@ { public: virtual void Play() = 0; virtual void GetNextFrame(double dt, void *target_surface) = 0; + bool Stopped(); + bool End_file(); }; class Player @@ -19,8 +21,17 @@ Player(); virtual ~Player(); - ITrack *LoadTrack(const wchar_t *name); IMovie *LoadMovie(const wchar_t *name, int width, int height, int cache_ms); + IMovie *LoadMovieFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height); + bool LoadFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height); + char *DoFrame(); + }; -}; \ No newline at end of file +}; + +extern bool end_current_file; +extern bool loop_current_file; +extern DWORD time_video_begin; +extern int current_movie_width; +extern int current_movie_height;
--- a/UI/UIHouses.cpp Wed Apr 02 23:21:34 2014 +0200 +++ b/UI/UIHouses.cpp Mon Apr 07 19:15:13 2014 +0600 @@ -881,7 +881,7 @@ uTextureID_right_panel_loop = uTextureID_right_panel; if ( uNumDialogueNPCPortraits == 1 ) pDialogueNPCCount = 1; - pVideoPlayer->OpenHouseMovie(pAnimatedRooms[uCurrentHouse_Animation].video_name, 1u); + pVideoPlayer->OpenHouseMovie(pAnimatedRooms[uCurrentHouse_Animation].video_name, 1u); dword_5C35D4 = 1; if ( (signed int)uHouseID < 139 || (signed int)uHouseID > 172 ) {
--- a/VideoPlayer.cpp Wed Apr 02 23:21:34 2014 +0200 +++ b/VideoPlayer.cpp Mon Apr 07 19:15:13 2014 +0600 @@ -8,6 +8,7 @@ #include "Mouse.h" #include "VideoPlayer.h" +#include "MediaPlayer.h" #include "AudioPlayer.h" #include "Game.h" #include "Render.h" @@ -357,7 +358,8 @@ HDC back_dc = CreateCompatibleDC(dc); HBITMAP back_bmp; SelectObject(back_dc, back_bmp = CreateCompatibleBitmap(dc, client_width, client_height)); - + DWORD t = GetTickCount(); + end_current_file = false; while (true) { if ( pVideoPlayer->bStopBeforeSchedule ) @@ -373,9 +375,17 @@ } GUI_MainMenuMessageProc(); - if (pMovie->Stopped()) + double dt = (GetTickCount() - t) / 1000.0; + //dt = 1.0/15.0; + t = GetTickCount(); + + //log("dt=%.5f\n", dt); + + auto image = new char[client_width * client_height * 4]; + + pMovie->GetNextFrame(dt, image); + if (end_current_file) break; - char *image = pPlayer->DoFrame(); if (image) { // draw to hwnd @@ -862,9 +872,9 @@ void VideoPlayer::UpdatePalette() { Log::Warning(L"smacker"); - + loop_current_file = true; pRenderer->BeginScene(); - if (pMovie->Stopped())//видео завершено/перезагрузка + /*if (pMovie->Stopped())//видео завершено/перезагрузка { int width = game_viewport_width; int height = game_viewport_height; @@ -872,11 +882,21 @@ SetFilePointer(hVidFile, uOffset, nullptr, FILE_BEGIN); - pMovie = pPlayer->LoadMovieFromLOD(hVidFile, &readFunction, &seekFunction, width, height); + //pMovie = pPlayer->LoadMovieFromLOD(hVidFile, &readFunction, &seekFunction, width, height); } - else + else */ { - char *image = pPlayer->DoFrame(); + 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->GetNextFrame(dt, image); + Sleep(70); //Ritor1:it's my include + int image_array[460 * 344];//game_viewport_width * game_viewport_height if (image) { @@ -1041,6 +1061,7 @@ } LoadMovie(pMovieName); + time_video_begin = GetTickCount(); /* this->pSmackerMovie = OpenSmack(Str2); if ( !this->pSmackerMovie ) @@ -1237,22 +1258,23 @@ //RGBTexture::RGBTexture(&pVideoPlayer->pVideoFrame); bStopBeforeSchedule = false; pResetflag = 0; - pPlayer = new MultimediaPlayer(); - pPlayer->Initialize(); + //pPlayer = new MultimediaPlayer(); + //pPlayer->Initialize(); + pPlayer = new Media::Player;//создаётся плеер pMovie = nullptr; //pBinkMovie = nullptr; } -bool MultimediaPlayer::libavcodec_initialized = false; +//bool MultimediaPlayer::libavcodec_initialized = false; -void MultimediaPlayer::Logger(void *, int, const char *format, va_list args) +/*void MultimediaPlayer::Logger(void *, int, const char *format, va_list args) { char msg[1024]; vsprintf(msg, format, args); DWORD w; WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), msg, lstrlenA(msg), &w, nullptr); -} +} */ int VideoPlayer::readFunction(void* opaque, uint8_t* buf, int buf_size) { @@ -1343,4 +1365,14 @@ pMovie = pPlayer->LoadMovieFromLOD(hVidFile, &readFunction, &seekFunction, client_width, client_height); +} +void VideoPlayer::PlayAudio(const wchar_t * pFilename) +{ + Media::ITrack *track = pPlayer->LoadTrack(pFilename); + track->Play(); +} +void VideoPlayer::PlayMovie(const wchar_t * pFilename) +{ + Media::IMovie *track = pPlayer->LoadMovie(pFilename, 640, 480, 0); + track->Play(); } \ No newline at end of file
--- a/VideoPlayer.h Wed Apr 02 23:21:34 2014 +0200 +++ b/VideoPlayer.h Mon Apr 07 19:15:13 2014 +0600 @@ -1,6 +1,7 @@ #pragma once #include "OSWindow.h" #include "Texture.h" +#include "MediaPlayer.h" @@ -388,6 +389,10 @@ do { int ret; +//Функция avcodec_decode_video2 осуществляет декодирование пакета в фрейм с использованием кодека, +//который мы получили раньше (codec_context). Функция устанавливает положительное значение frame_finished в случае +//если фрейм декодирован целиком (то есть один фрейм может занимать несколько пакетов и frame_finished будет +//установлен только при декодировании последнего пакета). if ((ret = avcodec_decode_video2(dec_ctx, f, (int *)&done, p)) < 0) return ret; } while (!done); @@ -409,10 +414,10 @@ { if (av_image_alloc(out_data, out_linesize, dst_width, dst_height, format, 1) < 0) return false; - +// создание контекста для преобразования SwsContext *converter = sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format, - dst_width, dst_height, format, - SWS_BICUBIC, nullptr, nullptr, nullptr); + dst_width, dst_height, format, SWS_BICUBIC, nullptr, nullptr, nullptr); +// преобразование кадра sws_scale(converter, frame->data, frame->linesize, 0, frame->height, out_data, out_linesize); sws_freeContext(converter); @@ -444,6 +449,7 @@ volatile int done = false; do { +//Декодирование аудио-пакета осуществляется функцией avcodec_decode_audio4 int ret; if ((ret = avcodec_decode_audio4(dec_ctx, f, (int *)&done, p)) < 0) return ret; @@ -522,7 +528,7 @@ class MovieCached { public: - bool Stopped() { return stopped; } + //bool Stopped() { return stopped; } int GetWidth() { return width; } int GetHeight() { return height; } inline ~MovieCached() @@ -553,7 +559,7 @@ avioContext = nullptr; } - bool LoadFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height) + /*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(16384 + FF_INPUT_BUFFER_PADDING_SIZE); // can get av_free()ed by libav @@ -563,17 +569,28 @@ format_ctx = avformat_alloc_context(); format_ctx->pb = avioContext; return Load("dummyFilename", width, height); - } + } */ - bool Load(const char *video_filename, int width, int height) + /*bool Load(const char *video_filename, int width, int height) { this->width = width; this->height = height; - +// Open video file +//откроем входной файл(Шаг 2) +//Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре +//AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует +//автоматическое определение параметров. if (avformat_open_input(&format_ctx, video_filename, nullptr, nullptr) >= 0) { +// Retrieve stream information +//Т.к. avformat_open_input читает только заголовок файла, то следующим шагом нужно получить информацию о потоках +//в файле. Это делается функцией avformat_find_stream_info.(Шаг 3) if (avformat_find_stream_info(format_ctx, nullptr) >= 0) { +// Dump information about file onto standard error +//После этого format_context->streams содержит все существующие потоки файла. +//Их количество равно format_context->nb_streams. +//Вывести подробную информацию о файле и обо всех потоках можно функцией av_dump_format. av_dump_format(format_ctx, 0, video_filename, 0); video_stream_idx = OpenStream(AVMEDIA_TYPE_VIDEO, &video_stream, &video_stream_dec, &video_stream_dec_ctx); @@ -590,13 +607,17 @@ return Release(), false; strcpy(movie_name, video_filename); +//Данные из файла читаются пакетами (AVPacket) packet = new AVPacket; av_init_packet(packet); 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 Release() { @@ -612,6 +633,8 @@ video_stream_idx = -1; video_stream = nullptr; video_stream_dec = nullptr; + + // закрытие видео кодека avcodec_close(video_stream_dec_ctx); video_stream_dec_ctx = nullptr; } @@ -621,6 +644,8 @@ audio_stream_idx = -1; audio_stream = nullptr; audio_stream_dec = nullptr; + + // закрытие аудио кодека avcodec_close(audio_stream_dec_ctx); } @@ -638,13 +663,15 @@ } - int OpenStream(AVMediaType type, AVStream **out_stream, AVCodec **out_dec, AVCodecContext **out_dec_ctx) + /*int OpenStream(AVMediaType type, AVStream **out_stream, AVCodec **out_dec, AVCodecContext **out_dec_ctx) { int stream_idx = av_find_best_stream(format_ctx, type, -1, -1, nullptr, 0); if (stream_idx < 0) return stream_idx; auto stream = format_ctx->streams[stream_idx]; +//Информация о кодеке в потоке называется «контекстом кодека» (AVCodecContext). +//Используя эту информацию, мы можем найти необходимый кодек (AVCodec) и открыть его. auto dec_ctx = stream->codec; auto dec = avcodec_find_decoder(dec_ctx->codec_id); if (dec) @@ -657,8 +684,9 @@ return stream_idx; } } + fprintf(stderr, "ffmpeg: Unable to open codec\n"); return -1; - } + } */ MultimediaFrame::Ptr GetNextFrame() { @@ -666,6 +694,7 @@ packet->size = 0; volatile int got_frame = false; + //чтение пакетов do { if (av_read_frame(format_ctx, packet) < 0) @@ -673,8 +702,8 @@ stopped = true; return nullptr; } - } while (packet->stream_index != video_stream_idx && - packet->stream_index != audio_stream_idx); + } while (packet->stream_index != video_stream_idx && //пока пакет не пренадлежит к видеопотоку + packet->stream_index != audio_stream_idx); //и не принадлежит аудиопотоку if (packet->stream_index == video_stream_idx) return MultimediaFrame::Ptr(new MultimediaVideoFrame(AVMEDIA_TYPE_VIDEO, packet, video_stream_dec_ctx, width, height)); @@ -688,7 +717,7 @@ char movie_name[256]; int width; int height; - bool stopped; + //bool stopped; AVFormatContext *format_ctx; //AVFrame *frame; AVPacket *packet; @@ -696,23 +725,23 @@ OpenALSoundProvider *sound_provider; int video_stream_idx; - AVStream *video_stream; + AVStream *video_stream;//содержат информацию о видео потоке AVCodec *video_stream_dec; AVCodecContext *video_stream_dec_ctx; int audio_stream_idx; - AVStream *audio_stream; + AVStream *audio_stream;//содержат информацию о аудио потоке AVCodec *audio_stream_dec; AVCodecContext *audio_stream_dec_ctx; unsigned char * ioBuffer; AVIOContext *avioContext; }; -typedef MovieCached<10> Movie; +//typedef MovieCached<10> Movie; -class MultimediaPlayer +/*class MultimediaPlayer { public: inline MultimediaPlayer() @@ -725,6 +754,11 @@ { av_log_set_callback(Logger); avcodec_register_all(); + +// Register all available file formats and codecs +//инициализируем библиотеку ffmpeg(Шаг 1) +//Во время инициализации регистрируются все имеющиеся в библиотеке форматы файлов и кодеков. +//После этого они будут использоваться автоматически при открытии файлов этого формата и с этими кодеками. av_register_all(); libavcodec_initialized = true; @@ -736,7 +770,7 @@ return true; } - Movie *LoadMovieFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height) + /*Movie *LoadMovieFromLOD(HANDLE h, int readFunction(void*, uint8_t*, int), int64_t seekFunction(void*, int64_t, int), int width, int height) { auto movie = new Movie(sound_provider); if (movie) @@ -758,9 +792,9 @@ delete movie; } return nullptr; - } + } */ - Movie *LoadMovie(const char *filename, int width, int height) + /*Movie *LoadMovie(const char *filename, int width, int height) { auto movie = new Movie(sound_provider); if (movie) @@ -774,7 +808,7 @@ delete movie; } return nullptr; - } + } inline char *DoFrame() { @@ -821,12 +855,12 @@ static void Logger(void *, int, const char *format, va_list args); OpenALSoundProvider *sound_provider; - Movie *current_movie; + //Movie *current_movie; int current_movie_width; int current_movie_height; static bool libavcodec_initialized; -}; +};*/ @@ -900,8 +934,8 @@ char pCurrentMovieName[64]; char pVideoFrameTextureFilename[32]; int field_104; - MultimediaPlayer *pPlayer; - Movie *pMovie; + Media::Player *pPlayer; + Media::IMovie *pMovie; HANDLE hVidFile; int uSize; int uOffset; @@ -909,6 +943,8 @@ static int readFunction(void *, uint8_t *, int); static int64_t seekFunction(void *, int64_t, int); void LoadMovie(const char *); + void PlayAudio(const wchar_t * pFilename); + void PlayMovie(const wchar_t * pFilename); }; #pragma pack(pop)