Mercurial > mm7
diff MediaPlayer.cpp @ 2347:d57505d3c70c
VideoPlayer cleaning
author | Ritor1 |
---|---|
date | Tue, 08 Apr 2014 17:39:59 +0600 |
parents | 182effc4b0ee |
children | 08c4f1799ca1 |
line wrap: on
line diff
--- a/MediaPlayer.cpp Mon Apr 07 19:15:31 2014 +0600 +++ b/MediaPlayer.cpp Tue Apr 08 17:39:59 2014 +0600 @@ -22,8 +22,7 @@ #include "MediaPlayer.h" using namespace Media; - - + class MemoryStream { public: @@ -90,7 +89,7 @@ current_pos = data_size; } - inline size_t Unwind(size_t bytes) + inline size_t Unwind(size_t bytes)//зациклить??? { if (bytes > current_pos) current_pos = 0; @@ -118,7 +117,6 @@ size_t current_pos; }; - bool end_current_file; bool loop_current_file; DWORD time_video_begin; @@ -127,8 +125,6 @@ OpenALSoundProvider *provider = nullptr; - - static int av_num_bytes_per_sample(AVSampleFormat sample_fmt) { switch (sample_fmt) @@ -158,8 +154,6 @@ return 0; } - - struct AVStreamWrapper { inline AVStreamWrapper() @@ -179,6 +173,7 @@ dec = nullptr; if (dec_ctx) { + // Close the codec // закрытие видео кодека avcodec_close(dec_ctx); dec_ctx = nullptr; @@ -218,19 +213,23 @@ static bool av_open_stream(AVFormatContext *format_ctx, AVMediaType type, AVStreamWrapper *out_stream) { -// номер видеопотока + // найдем первый 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) и открыть его. + //Информация о кодеке в потоке называется «контекстом кодека» (AVCodecContext). + //Используя эту информацию, мы можем найти необходимый кодек (AVCodec) и открыть его. + // получаемм кодек 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; @@ -240,10 +239,14 @@ 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"); + fprintf(stderr, "ffmpeg: Unable to open codec\n");// Кодек не найден + return false; } - return false; + fprintf(stderr, "ffmpeg: Didn't find a stream\n");// Не найден stream + return false; } static bool av_open_audio_stream(AVFormatContext *format_ctx, AVAudioStream *out_stream) @@ -273,8 +276,6 @@ return true; } - - void InterleaveAudioData(MemoryStream *stream, AVSampleFormat src_format, int num_channels, int num_samples, uint8_t **channels) { unsigned int bytes_per_sample; @@ -343,14 +344,12 @@ } } - - bool DecodeAudioFrame(AVCodecContext *dec_ctx, AVPacket *avpacket, AVFrame *avframe, MemoryStream *out_audio_data, int *out_num_audio_samples) { volatile int decoded = false; do { -//Декодирование аудио-пакета осуществляется функцией avcodec_decode_audio4 + //Декодирование аудио-пакета осуществляется функцией avcodec_decode_audio4 if (avcodec_decode_audio4(dec_ctx, avframe, (int *)&decoded, avpacket) < 0) { log("Cannot decode audio frame\n"); @@ -469,22 +468,26 @@ 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(); -//воспроизведение audio. -//Данные из файла читаются пакетами (AVPacket), а для воспроизведения используется фрейм (AVFrame). + //Чтение аудио данных. + //Данные из файла читаются пакетами (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) { + // Is this a packet from the audio stream? //Принадлежит ли пакет к аудиопотоку if (packet->stream_index != audio_stream_idx) { @@ -492,6 +495,8 @@ continue; } + // Decode audio frame + //Декодируем аудио фрейм int num_samples_decoded; DecodeAudioFrame(dec_ctx, packet, frame, out_audio_stream, &num_samples_decoded); @@ -501,13 +506,13 @@ *out_num_audio_frames = num_audio_frames; *out_num_audio_samples = num_audio_samples; + //Освободить память выделенную для фрейма avcodec_free_frame(&frame); delete packet; return true; } - class Track: public Media::ITrack { public: @@ -533,28 +538,29 @@ } } - bool Load(const wchar_t *filename) + bool LoadAudio(const wchar_t *filename) //Загрузка из папки для mp3 { char filenamea[1024]; sprintf(filenamea, "%S", filename); -// Open audio file -//откроем входной файл(Шаг 2) -//Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре -//AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует -//автоматическое определение параметров. + // 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. + // 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. + // 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)) { Error("Cannot open strack: %s", filenamea); @@ -564,6 +570,8 @@ 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 @@ -587,10 +595,10 @@ } } Release(); - fprintf(stderr, "ffmpeg: Unable to find stream info\n"); + fprintf(stderr, "ffmpeg: Unable to find stream info\n"); //Не найден поток return false; } - fprintf(stderr, "ffmpeg: Unable to open input file\n"); + fprintf(stderr, "ffmpeg: Unable to open input file\n"); //Не может открыть файл return false; } @@ -598,8 +606,7 @@ { provider->PlayTrack16(device_buffer, loop); } - - + protected: AVFormatContext *format_ctx; AVAudioStream audio; @@ -610,8 +617,6 @@ OpenALSoundProvider::TrackBuffer *device_buffer; }; - - class Movie: public Media::IMovie { public: @@ -624,8 +629,6 @@ 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() { @@ -648,8 +651,7 @@ format_ctx = nullptr; avioContext = nullptr; } - - + inline void Release() { ReleaseAVCodec(); @@ -661,13 +663,14 @@ video.Release(); if (format_ctx) { - // закрытия контекста файла + // Close the video file + // закрытие контекста файла(видео файла) av_close_input_file(format_ctx); format_ctx = nullptr; } } - bool Load(const wchar_t *filename, int dst_width, int dst_height, int cache_ms) + bool Load(const wchar_t *filename, int dst_width, int dst_height, int cache_ms) //Загрузка { char filenamea[1024]; sprintf(filenamea, "%S", filename); @@ -675,24 +678,27 @@ width = dst_width; height = dst_height; -// Open video file -//откроем входной файл(Шаг 2) -//Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре -//AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует -//автоматическое определение параметров. + // Open video file + //откроем входной файл(Шаг 2) + //Функция avformat_open_input читает файловый заголовок и сохраняет информацию о найденных форматах в структуре + //AVFormatContext. Остальные аргументы могут быть установлены в NULL, в этом случае libavformat использует + //автоматическое определение параметров. Последние 2 аргумента используются для формата файла и опций. if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) { -// Retrieve stream information -//Т.к. avformat_open_input читает только заголовок файла, то следующим шагом нужно получить информацию о потоках -//в файле. Это делается функцией avformat_find_stream_info.(Шаг 3) + // 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. + // Dump information about file onto standard error + //Инициализируем pFormatCtx->streams + //После этого format_context->streams содержит все существующие потоки файла. + //Их количество равно format_context->nb_streams. + //Вывести подробную информацию о файле и обо всех потоках можно функцией av_dump_format. av_dump_format(format_ctx, 0, filenamea, 0); + //pFormatCtx->streams - массив указателей, размера pFormatCtx->nb_streams, поищем тут потоки. if (!av_open_audio_stream(format_ctx, &audio)) { Error("Cannot open audio stream: %s", filenamea); @@ -704,21 +710,25 @@ Error("Cannot open video stream: %s", filenamea); return Release(), false; } - - if (_stricmp("binkvideo", video.dec->name) ) - { + + //Ritor1: include + if (_stricmp("binkvideo", video.dec->name) ) + { current_movie_width = video.dec_ctx->width; current_movie_height = video.dec_ctx->height; - } - else - { + } + else + { current_movie_width = width; current_movie_height = height; - } + } + // decoding_packet = new AVPacket; av_init_packet(decoding_packet); + // Allocate video frame + //Выделим память для фрейма decoding_frame = avcodec_alloc_frame(); audio_data_in_device = provider->CreateStreamingTrack16(audio.dec_ctx->channels, audio.dec_ctx->sample_rate, 2); @@ -726,11 +736,12 @@ return true; } fprintf(stderr, "ffmpeg: Unable to find stream info\n"); - return false; + return Release(), false; // Не найдена информация о потоке } fprintf(stderr, "ffmpeg: Unable to open input file\n"); - return false; + 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) @@ -743,91 +754,15 @@ 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() - { - } - - virtual void GetNextFrame(double dt, void *dst_surface)// рисует сразу на экран + virtual void GetNextFrame(double dt, void *dst_surface)// Получить следующий фрейм { playback_time += dt;//изменение времени -//Данные из файла читаются пакетами (AVPacket), а для отображения используется фрейм (AVFrame). -//(помните мы сохранили номер видео потока в переменной video_stream). + //Данные из файла читаются пакетами (AVPacket), а для отображения используется фрейм (AVFrame). 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); @@ -837,47 +772,32 @@ return; } - volatile int decoded = false; + volatile int frameFinished = false; //чтение пакетов - // keep reading packets until we hit the end or find a video packet + // 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 (loop_current_file) - { - av_seek_frame(format_ctx, video.stream_idx, 0, AVSEEK_FLAG_ANY); - } - else - { - // probably movie is finished - //__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()); + provider->Stream16(audio_data_in_device, num_audio_samples, audio_data.Ptr()); //отправляем на воспроизведение continue; } // Decode video frame //пакет к видеопотоку else if (avpacket->stream_index == video.stream_idx) { -//Функция avcodec_decode_video2 осуществляет декодирование пакета в фрейм с использованием кодека, -//который мы получили раньше (codec_context). Функция устанавливает положительное значение frame_finished в случае -//если фрейм декодирован целиком (то есть один фрейм может занимать несколько пакетов и frame_finished будет -//установлен только при декодировании последнего пакета). - avcodec_decode_video2(video.dec_ctx, avframe, (int *)&decoded, avpacket); - if (decoded) + //Функция avcodec_decode_video2 осуществляет декодирование пакета в фрейм с использованием кодека, + //который мы получили раньше (codec_context). Функция устанавливает положительное значение frame_finished в случае + //если фрейм декодирован целиком (то есть один фрейм может занимать несколько пакетов и frame_finished будет + //установлен только при декодировании последнего пакета). + avcodec_decode_video2(video.dec_ctx, avframe, (int *)&frameFinished, avpacket); + if (frameFinished) { if (last_resampled_frame_data[0]) av_freep(&last_resampled_frame_data[0]); @@ -891,10 +811,11 @@ 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); + //копирование в возвращаемую переменную memcpy(dst_surface, rescaled_data[0], current_movie_height * rescaled_linesize[0]); last_resampled_frame_num = desired_frame_number; @@ -903,12 +824,13 @@ } } else - memset(dst_surface, 0, current_movie_width * current_movie_height * 4); + memset(dst_surface, 0, current_movie_width * current_movie_height * 4); //записать 0 в выходную переменную } return; }// while (avpacket->stream_index != video.stream_idx || // avpacket->pts != desired_frame_number); - if (loop_current_file) + //завершение пакетов + if (loop_current_file) //зациклен ли файл { av_seek_frame(format_ctx, video.stream_idx, 0, AVSEEK_FLAG_ANY); } @@ -922,44 +844,9 @@ } } - /*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; - } - }*/ + virtual void Play() + { + } protected: char movie_filename[256]; @@ -984,13 +871,12 @@ int last_resampled_frame_num; uint8_t *last_resampled_frame_data[4]; int last_resampled_frame_linesize[4]; -}; +}; - -ITrack *Player::LoadTrack(const wchar_t *filename) +ITrack *Player::LoadTrack(const wchar_t *filename) //Загрузить mp3 { auto track = new Track; - if (!track->Load(filename)) + if (!track->LoadAudio(filename)) { delete track; track = nullptr; @@ -998,8 +884,7 @@ return track; } - -IMovie *Player::LoadMovie(const wchar_t *filename, int width, int height, int cache_ms) +IMovie *Player::LoadMovie(const wchar_t *filename, int width, int height, int cache_ms) //Загрузить видео { auto movie = new Movie; if (!movie->Load(filename, width, height, cache_ms)) @@ -1010,10 +895,17 @@ return movie; } - - - - +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)) + return movie; + delete movie; + } + return nullptr; +} void av_logger(void *, int, const char *format, va_list args) { @@ -1035,10 +927,10 @@ av_log_set_callback(av_logger); avcodec_register_all(); -// Register all available file formats and codecs -//инициализируем библиотеку ffmpeg(Шаг 1) -//Во время инициализации регистрируются все имеющиеся в библиотеке форматы файлов и кодеков. -//После этого они будут использоваться автоматически при открытии файлов этого формата и с этими кодеками. + // Register all available file formats and codecs + //инициализируем библиотеку ffmpeg(Шаг 1) + //Во время инициализации регистрируются все имеющиеся в библиотеке форматы файлов и кодеков. + //После этого они будут использоваться автоматически при открытии файлов этого формата и с этими кодеками. av_register_all(); libavcodec_initialized = true; @@ -1054,26 +946,3 @@ Player::~Player() { } - 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