FFMPEG video player - OGRE window, SDL audio

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
Post Reply
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

New thread with updated version: http://www.ogre3d.org/forums/viewtopic. ... 34&start=0

Original message preserved below.
-----------------------------------------------------

It was originally based on this C tutorial. I removed the SDL parts (except SDL_audio) and replaced them with boost thread & Ogre window output. The dependencies are:

Dependencies:
- SDL_audio
- Ogre
- boost thread
- ffmpeg

Features:
- AV sync
- Multithreaded decoding
- Reads video from Ogre resource

ToDo:
- Frame dropping
- Seeking (it was broken in the tutorial, and I didn't need it anyway)
- Play/Pause

How to use:
the VideoPlayer class puts the video in "VideoTexture" and "VideoMaterial" which you can then display on a cube, or quad, or whatever you like.

Link: https://github.com/scrawl/ogre-ffmpeg-videoplayer

Its possible that I'll post a version with OpenAL audio instead of SDL in the near future.
Last edited by scrawl on Sat Oct 25, 2014 9:56 pm, edited 1 time in total.
TheSHEEEP
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 972
Joined: Mon Jun 02, 2008 6:52 pm
Location: Berlin
x 65

Re: FFMPEG video player - OGRE window, SDL audio

Post by TheSHEEEP »

Nice, I had planned to do something along those lines in the future, too, as I just happen to work with FFmpeg at work. And Ogre is missing a good video plugin.

I think it would be best for audio if there was no dependency for it at all. Just give raw audio data and let the user decide what to do with it. And probably offer a sample that shows how to do it with OpenAL/SDL/whatever.
While the ffmpeg dependency makes sense, any audio dependency will probably interfere with whatever the application is using for audio (FMOD, OpenAL, irrKlang, etc.).

I'd prefer to make it OO-though, as that C style just breaks with the normal Ogre flow. That would even make it easier to transform it into an actual Ogre plugin. It would also be much easier to understand & use, IMO.
Besides, I know that FFmpeg sources (and any online samples that use FFmpeg C API) use abysmally small variable names and hard to understand comments (if at all) and code, but please, for the love of god, do not inherit that style.
Reading variable names like "q" outside of an indexing variable in a loop in public code makes little sheep cry :cry: (and that is after having worked with ffmpeg for almost 2 years now). That "typical" C style just makes code a pain to read and understand. Remember: Code is by far more often read than written. So making it easy to read and understand is more important than saving bytes for the cpp file ;)

FFmpeg generally is still somewhere in the 1990s when it comes to usability & support. I mean, hell, the only way to communicate with users & devs is a mailing list. Imagine if Ogre did that :lol:
And don't get me started on the debugging with various codecs :/ By the way, which video types &codecs does your player play?

Unfortunately, FFmpeg is the only alternative to commercial video decoding & encoding libraries. But when ignoring the usability issues, it is actually very good & very fast.
My site! - Have a look :)
Also on Twitter - extra fluffy
bstone
OGRE Expert User
OGRE Expert User
Posts: 1920
Joined: Sun Feb 19, 2012 9:24 pm
Location: Russia
x 201

Re: FFMPEG video player - OGRE window, SDL audio

Post by bstone »

Thanks, scrawl. Your post gave me an idea for one little thing that stopped me from implementing another little thing that I think might be good to have in Ogre :D Just need to find a few spare hours and implement it.
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

TheSHEEEP wrote: By the way, which video types &codecs does your player play?
In theory, everything that FFMPEG supports, and FFMPEG supports almost everything. (even the exotic formats - I successfully played BINK video (.bik) with it.)

I've also tested .mkv files with x264 and webm encoding. HD videos (720p) also worked.

For some reason, it fails to open .avi files (but ffplay can play them). It seems to be a problem reading it into the OGRE resource.
It would probably work again if one would use primitive file loading instead of ogre resource. But I don't have any intention doing that.
TheSHEEEP
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 972
Joined: Mon Jun 02, 2008 6:52 pm
Location: Berlin
x 65

Re: FFMPEG video player - OGRE window, SDL audio

Post by TheSHEEEP »

scrawl wrote:
TheSHEEEP wrote: By the way, which video types &codecs does your player play?
In theory, everything that FFMPEG supports, and FFMPEG supports almost everything. (even the exotic formats - I successfully played BINK video (.bik) with it.)
Trust me, that is not the case, for some formats you have to do additional stuff. But it is more than enough if it plays the "usual suspects" :)
It should probably play the open formats (like ogg video), as I don't know the license situation if you want to publish something like avi or mp4 with an application.
My site! - Have a look :)
Also on Twitter - extra fluffy
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

Hm.. I tried some ogg videos and they don't work. Will look into it.
TheSHEEEP
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 972
Joined: Mon Jun 02, 2008 6:52 pm
Location: Berlin
x 65

Re: FFMPEG video player - OGRE window, SDL audio

Post by TheSHEEEP »

Here, this should help with debugging:

Code: Select all

static void log_callback(void *ptr, int level, const char *fmt, va_list vargs)
{
	static char message[8192];   
	const char *module = NULL;

	// Get module name
    if (ptr)
    {
        AVClass *avc = (AVClass*) ptr;
        module = avc->item_name(ptr);
    }

	// Create the actual message
	vsnprintf(message, sizeof(message), fmt, vargs);

	// Append the message to the logfile
	if (module)
	{
		std::cout << module << " -------------------" << std::endl;
	}
	std::cout << "lvl: " << level << std::endl << "msg: " << message << std::endl;
}

// And somewhere after ffmpeg initialization
av_log_set_callback(log_callback);
av_log_set_level(AV_LOG_WARNING);
My site! - Have a look :)
Also on Twitter - extra fluffy
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

I changed to usual file input (instead of ogre resource) and everything works now, including ogg and avi. Strange...
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

Ok, there was a mistake in the resource access functions. Code updated. If anyone finds a video that still does not work, please let me know :)
nickG
Greenskin
Posts: 122
Joined: Fri Jan 20, 2012 6:44 pm
Location: Russia,Moscow
x 1

Re: FFMPEG video player - OGRE window, SDL audio

Post by nickG »

do you can make port under windows?
User avatar
Mind Calamity
Ogre Magi
Posts: 1255
Joined: Sat Dec 25, 2010 2:55 pm
Location: Macedonia
x 81

Re: FFMPEG video player - OGRE window, SDL audio

Post by Mind Calamity »

nickG wrote:do you can make port under windows?
All that the video player uses is cross-platform, so you can use it on most major platforms, including Windows. Just use CMake to generate the Visual Studio (or your favorite IDE) project files,


Great work, scrawl. This is sure to come in handy sometime in the future :)
BitBucket username changed to iboshkov (from MindCalamity)
Do you need help? What have you tried?
- xavier
---------------------
HkOgre - a Havok Integration for OGRE | Simple SSAO | My Blog | My YouTube | My DeviantArt
dbtx
Halfling
Posts: 66
Joined: Tue Nov 01, 2011 9:07 pm
x 10

Re: FFMPEG video player - OGRE window, SDL audio

Post by dbtx »

This is pretty cool, works great, but what should be the license? It's not mentioned anywhere in the repo... The tutorial you linked to says it was in turn based on ffplay which is under LGPL along with all of ffmpeg, so is that it?
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

The tutorial you linked to says it was in turn based on ffplay which is under LGPL along with all of ffmpeg, so is that it?
Yeah, sure.

Please note there is a much improved version here: .hpp .cpp. It's more OOP, fixes a ton of bugs from the previous version (that caused some videos to fail syncing or to play at all), and uses OpenAL instead of SDL. It depends on some OpenMW parts though (for the sound decoding & playback mainly), I have not found the time to make it general-purpose. Many thanks to Chris Robinson who did almost all of these improvements.
The new version is currently GPL3 as part of OpenMW source (but you might be able to convince us to relicense it separately, if necessary :) ).

Ideally, there would be several MovieAudioDecoder classes, one for SDL, one for OpenAL, one that wraps around OpenMW sound decoder (in our case) etc so the code is truly portable.
Fuentes
Gnoblar
Posts: 4
Joined: Thu Dec 27, 2012 7:18 am

Re: FFMPEG video player - OGRE window, SDL audio

Post by Fuentes »

scrawl wrote:
The tutorial you linked to says it was in turn based on ffplay which is under LGPL along with all of ffmpeg, so is that it?
Yeah, sure.

Please note there is a much improved version here: .hpp .cpp. It's more OOP, fixes a ton of bugs from the previous version (that caused some videos to fail syncing or to play at all), and uses OpenAL instead of SDL. It depends on some OpenMW parts though (for the sound decoding & playback mainly), I have not found the time to make it general-purpose. Many thanks to Chris Robinson who did almost all of these improvements.
The new version is currently GPL3 as part of OpenMW source (but you might be able to convince us to relicense it separately, if necessary :) ).

Ideally, there would be several MovieAudioDecoder classes, one for SDL, one for OpenAL, one that wraps around OpenMW sound decoder (in our case) etc so the code is truly portable.
How to convince you? This class really be a very good help for much of us. Right now i try to adapt your code to fit my app, but is very hard do it to me because i'm not Ogre expert, so on every three steps that i make, two are wrong... Damn me!!

Anyway, your code snippet was very helpfull to me for understand the internals of Ogre, but if you release an standalone version of this... That would be really awesome!!

Many thanks for your already done work... And for the future release :P!!
CHRISPman
Gnoblar
Posts: 6
Joined: Fri Aug 02, 2013 6:47 am

Re: FFMPEG video player - OGRE window, SDL audio

Post by CHRISPman »

Hey,
I'm rather new to Ogre and am making a game, it's going to require cut scenes. Your implementation seems to be one of the most adaptable at the current time that I can find, and it's also a lot newer than most (I don't seem to be able to get a version of Theora to work with Visual Studio 2010).

If you have been working on a standalone that would be amazing, or else have you ever experienced this error in compiling?

Code: Select all

videoplayer.obj : error LNK2019: unresolved external symbol "public: class MWBase::InputManager * __thiscall MWBase::Environment::getInputManager(void)const " (?getInputManager@Environment@MWBase@@QBEPAVInputManager@2@XZ) referenced in function "public: void __thiscall MWRender::VideoPlayer::playVideo(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,bool)" (?playVideo@VideoPlayer@MWRender@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_N@Z)
1>videoplayer.obj : error LNK2019: unresolved external symbol "public: class MWBase::SoundManager * __thiscall MWBase::Environment::getSoundManager(void)const " (?getSoundManager@Environment@MWBase@@QBEPAVSoundManager@2@XZ) referenced in function "public: void __thiscall MWRender::VideoPlayer::playVideo(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,bool)" (?playVideo@VideoPlayer@MWRender@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_N@Z)
1>videoplayer.obj : error LNK2019: unresolved external symbol "public: class MWBase::WindowManager * __thiscall MWBase::Environment::getWindowManager(void)const " (?getWindowManager@Environment@MWBase@@QBEPAVWindowManager@2@XZ) referenced in function "public: void __thiscall MWRender::VideoPlayer::playVideo(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,bool)" (?playVideo@VideoPlayer@MWRender@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_N@Z)
1>videoplayer.obj : error LNK2019: unresolved external symbol "public: static class MWBase::Environment const & __cdecl MWBase::Environment::get(void)" (?get@Environment@MWBase@@SAABV12@XZ) referenced in function "public: void __thiscall MWRender::VideoPlayer::playVideo(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,bool)" (?playVideo@VideoPlayer@MWRender@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_N@Z)
1>bin\Debug\\Flatland.exe : fatal error LNK1120: 4 unresolved externals
I know its a linking issue but have been told by a mate that it is potentially a double declaration, which would mean there is issues in the repo which doesn't make sense.

The way I have tried to add it is by:
* Getting the latest boost and linking to it in the general c++ additional headers and Linker additional headers (Compiling this with bjam)
* Putting open morrowind into a dir, and linking general c++ additional headers and Linker additional headers.
* adding the videoplayer.cpp and videoplayer.hpp files to my project.

If you can think of anything simple I have done silly please let me know as I have been sinking quite a bit of time into chasing my own tail on a few tasks now :/

thanks,
Chris
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

To solve those undefined references you need to add the cpp files that contain the definition of those functions to your project. However those cpp files will probably contain more references that need to be resolved and I wouldn't be surprised if you'd end up adding all of openmw's code.

Unfortunately I haven't found the time to make a standalone version, also because the sound code isn't mine and I'd need to understand it first.
CHRISPman
Gnoblar
Posts: 6
Joined: Fri Aug 02, 2013 6:47 am

Re: FFMPEG video player - OGRE window, SDL audio

Post by CHRISPman »

That's OK, looking through some of the code has been very informative. Thanks :)
nickG
Greenskin
Posts: 122
Joined: Fri Jan 20, 2012 6:44 pm
Location: Russia,Moscow
x 1

Re: FFMPEG video player - OGRE window, SDL audio

Post by nickG »

I tried to make a port without the use of boost.threads,only with C++11,but has got crash in destructor.(then video has finished)

Any ideas how a can fix it?

Code: Select all

#include "RenderPrivate.h"
//TODO:апдейтимся в основном цикле
//Вынести в рендер
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <time.h>
#include "VideoPlayer.h"

extern "C"
{
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>
#include <libswscale/swscale.h>
}
using namespace Ogre;

#ifdef WIN32
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")

#pragma comment(lib,"swscale.lib")
typedef SSIZE_T ssize_t;
#endif



namespace RenderModule
{

#ifdef COMPONENT_USE_FFMPEG



#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
#define AV_SYNC_THRESHOLD 0.01f
#define AUDIO_DIFF_AVG_NB 20
#define VIDEO_PICTURE_QUEUE_SIZE 1

	enum {
		AV_SYNC_AUDIO_MASTER,
		AV_SYNC_VIDEO_MASTER,
		AV_SYNC_EXTERNAL_MASTER,

		AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER
	};


	struct PacketQueue {
		PacketQueue()
			: first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0)
		{ }
		~PacketQueue()
		{
			clear();
		}

		AVPacketList *first_pkt, *last_pkt;
		volatile bool flushing;
		int nb_packets;
		int size;

		std::mutex mutex;
		std::condition_variable cond;

		void put(AVPacket *pkt);
		int get(AVPacket *pkt, VideoState *is);

		void flush();
		void clear();
	};

	struct VideoPicture {
		VideoPicture() : pts(0.0)
		{ }

		std::vector<uint8_t> data;
		float pts;
	};

	struct VideoState {
		VideoState()
			: format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT)
			, external_clock_base(0.0)
			, audio_st(NULL)
			, video_st(NULL), frame_last_pts(0.0f), frame_last_delay(0.0f),
			video_clock(0.0f), sws_context(NULL), rgbaFrame(NULL), pictq_size(0),
			pictq_rindex(0), pictq_windex(0)
			, refresh_rate_ms(10), refresh(false), quit(false), display_ready(false)
		{
			// Register all formats and codecs
			av_register_all();
		}

		~VideoState()
		{
			deinit();
		}

		void init(const std::string& resourceName);
		void deinit();

		int stream_open(int stream_index, AVFormatContext *pFormatCtx);

		bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height);

		static void video_thread_loop(VideoState *is);
		static void decode_thread_loop(VideoState *is);

		void video_display();
		void video_refresh_timer();

		int queue_picture(AVFrame *pFrame, float pts);
		float synchronize_video(AVFrame *src_frame, float pts);

		static void video_refresh(VideoState *is);


		float get_audio_clock()
		{
			return 0;/*Nick this->AudioTrack->getTimeOffset();*/
		}

		float get_video_clock()
		{
			return this->frame_last_pts;
		}

		float get_external_clock()
		{
			return ((uint64_t) av_gettime() - this->external_clock_base) / 1000000.0;
		}

		float get_master_clock()
		{
			if (this->av_sync_type == AV_SYNC_VIDEO_MASTER)
				return this->get_video_clock();
			if (this->av_sync_type == AV_SYNC_AUDIO_MASTER)
				return this->get_audio_clock();
			return this->get_external_clock();
		}


		static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size);
		static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size);
		static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence);


		Ogre::DataStreamPtr stream;
		AVFormatContext* format_ctx;

		int av_sync_type;
		uint64_t external_clock_base;

		AVStream**  audio_st;
		PacketQueue audioq;
		//Nick  MWBase::SoundPtr AudioTrack;

		AVStream**  video_st;
		float      frame_last_pts;
		float      frame_last_delay;
		float      video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame
		PacketQueue videoq;
		SwsContext*  sws_context;
		VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
		AVFrame*     rgbaFrame; // used as buffer for the frame converted from its native format to RGBA
		int          pictq_size, pictq_rindex, pictq_windex;
		std::mutex pictq_mutex;
		std::condition_variable pictq_cond;


		std::thread parse_thread;
		std::thread video_thread;

		std::thread refresh_thread;
		volatile int refresh_rate_ms;

		volatile bool refresh;
		volatile bool quit;
		volatile bool display_ready;
	};


	void PacketQueue::put(AVPacket *pkt)
	{
		AVPacketList *pkt1;
		pkt1 = (AVPacketList*) av_malloc(sizeof(AVPacketList));
		if (!pkt1) throw std::bad_alloc();
		pkt1->pkt = *pkt;
		pkt1->next = NULL;

		if (pkt1->pkt.destruct == NULL)
		{
			if (av_dup_packet(&pkt1->pkt) < 0)
			{
				av_free(pkt1);
				Error("[VideoPlayer]Failed to duplicate packet");
			}
			av_free_packet(pkt);
		}

		this->mutex.lock();

		if (!last_pkt)
			this->first_pkt = pkt1;
		else
			this->last_pkt->next = pkt1;
		this->last_pkt = pkt1;
		this->nb_packets++;
		this->size += pkt1->pkt.size;
		this->cond.notify_one();

		this->mutex.unlock();
	}

	int PacketQueue::get(AVPacket *pkt, VideoState *is)
	{
		std::unique_lock<std::mutex> lock(this->mutex);
		while (!is->quit)
		{
			AVPacketList *pkt1 = this->first_pkt;
			if (pkt1)
			{
				this->first_pkt = pkt1->next;
				if (!this->first_pkt)
					this->last_pkt = NULL;
				this->nb_packets--;
				this->size -= pkt1->pkt.size;

				*pkt = pkt1->pkt;
				av_free(pkt1);

				return 1;
			}

			if (this->flushing)
				break;
			this->cond.wait(lock);
		}

		return -1;
	}

	void PacketQueue::flush()
	{
		this->flushing = true;
		this->cond.notify_one();
	}

	void PacketQueue::clear()
	{
		AVPacketList *pkt, *pkt1;

		this->mutex.lock();
		for (pkt = this->first_pkt; pkt != NULL; pkt = pkt1)
		{
			pkt1 = pkt->next;
			av_free_packet(&pkt->pkt);
			av_freep(&pkt);
		}
		this->last_pkt = NULL;
		this->first_pkt = NULL;
		this->nb_packets = 0;
		this->size = 0;
		this->mutex.unlock();
	}

	class MovieAudioDecoder    //Nick  : public MWSound::Sound_Decoder
	{
		static void fail(const std::string &str)
		{
			Error(str.c_str());
		}

		struct AutoAVPacket : public AVPacket {
			AutoAVPacket(int size = 0)
			{
				if (av_new_packet(this, size) < 0)
					throw std::bad_alloc();
			}
			~AutoAVPacket()
			{
				av_free_packet(this);
			}
		};

		VideoState *mVideoState;
		AVStream *mAVStream;

		AutoAVPacket mPacket;
		AVFrame *mFrame;
		ssize_t mFramePos;
		ssize_t mFrameSize;

		float mAudioClock;

		/* averaging filter for audio sync */
		float mAudioDiffAccum;
		const float mAudioDiffAvgCoef;
		const float mAudioDiffThreshold;
		int mAudioDiffAvgCount;

		/* Add or subtract samples to get a better sync, return number of bytes to
		* skip (negative means to duplicate). */
		int synchronize_audio()
		{
			if (mVideoState->av_sync_type == AV_SYNC_AUDIO_MASTER)
				return 0;

			int sample_skip = 0;

			// accumulate the clock difference
			float diff = mVideoState->get_master_clock() - mVideoState->get_audio_clock();
			mAudioDiffAccum = diff + mAudioDiffAvgCoef * mAudioDiffAccum;
			if (mAudioDiffAvgCount < AUDIO_DIFF_AVG_NB)
				mAudioDiffAvgCount++;
			else
			{
				float avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef);
				if (fabs(avg_diff) >= mAudioDiffThreshold)
				{
					int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) *
						mAVStream->codec->channels;
					sample_skip = ((int) (diff * mAVStream->codec->sample_rate) * n);
				}
			}

			return sample_skip;
		}

		int audio_decode_frame(AVFrame *frame)
		{
			AVPacket *pkt = &mPacket;

			for (;;)
			{
				while (pkt->size > 0)
				{
					int len1, got_frame;

					len1 = avcodec_decode_audio4(mAVStream->codec, frame, &got_frame, pkt);
					if (len1 < 0) break;

					if (len1 <= pkt->size)
					{
						/* Move the unread data to the front and clear the end bits */
						int remaining = pkt->size - len1;
						memmove(pkt->data, &pkt->data[len1], remaining);
						av_shrink_packet(pkt, remaining);
					}

					/* No data yet? Look for more frames */
					if (!got_frame || frame->nb_samples <= 0)
						continue;

					mAudioClock += (float) frame->nb_samples /
						(float) mAVStream->codec->sample_rate;

					/* We have data, return it and come back for more later */
					return frame->nb_samples * mAVStream->codec->channels *
						av_get_bytes_per_sample(mAVStream->codec->sample_fmt);
				}
				av_free_packet(pkt);

				/* next packet */
				if (mVideoState->audioq.get(pkt, mVideoState) < 0)
					return -1;

				/* if update, update the audio clock w/pts */
				if ((uint64_t) pkt->pts != AV_NOPTS_VALUE)
					mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts;
			}
		}

		void open(const std::string&)
#ifdef _WIN32
		{
			fail(std::string("[VideoPlayer]Invalid call to ") + __FUNCSIG__);
		}
#else
		{ fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); }
#endif

		void close() { }

		std::string getName()
		{
			return mVideoState->stream->getName();
		}

		void rewind() { }

	public:
		MovieAudioDecoder(VideoState *is)
			: mVideoState(is)
			, mAVStream(*is->audio_st)
			, mFrame(avcodec_alloc_frame())
			, mFramePos(0)
			, mFrameSize(0)
			, mAudioClock(0.0)
			, mAudioDiffAccum(0.0)
			, mAudioDiffAvgCoef(exp(log(0.01 / AUDIO_DIFF_AVG_NB)))
			/* Correct audio only if larger error than this */
			, mAudioDiffThreshold(2.0 * 0.050/* 50 ms */)
			, mAudioDiffAvgCount(0)
		{ }
		virtual ~MovieAudioDecoder()
		{
			av_freep(&mFrame);
		}
#if 0
		void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type)
		{
			if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_U8)
				*type = MWSound::SampleType_UInt8;
			else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_S16)
				*type = MWSound::SampleType_Int16;
			else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_FLT)
				*type = MWSound::SampleType_Float32;
			else
				fail(std::string("[VideoPlayer]Unsupported sample format: ")+
				av_get_sample_fmt_name(mAVStream->codec->sample_fmt));

			if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_MONO)
				*chans = MWSound::ChannelConfig_Mono;
			else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_STEREO)
				*chans = MWSound::ChannelConfig_Stereo;
			else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_QUAD)
				*chans = MWSound::ChannelConfig_Quad;
			else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_5POINT1)
				*chans = MWSound::ChannelConfig_5point1;
			else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_7POINT1)
				*chans = MWSound::ChannelConfig_7point1;
			else if(mAVStream->codec->channel_layout == 0)
			{
				/* Unknown channel layout. Try to guess. */
				if(mAVStream->codec->channels == 1)
					*chans = MWSound::ChannelConfig_Mono;
				else if(mAVStream->codec->channels == 2)
					*chans = MWSound::ChannelConfig_Stereo;
				else
				{
					std::stringstream sstr("[VideoPlayer]Unsupported raw channel count: ");
					sstr << mAVStream->codec->channels;
					fail(sstr.str());
				}
			}
			else
			{
				char str[1024];
				av_get_channel_layout_string(str, sizeof(str), mAVStream->codec->channels,
					mAVStream->codec->channel_layout);
				fail(std::string("[VideoPlayer]Unsupported channel layout: ")+str);
			}

			*samplerate = mAVStream->codec->sample_rate;
		}
#endif

		size_t read(char *stream, size_t len)
		{
			int sample_skip = synchronize_audio();
			size_t total = 0;

			while (total < len)
			{
				if (mFramePos >= mFrameSize)
				{
					/* We have already sent all our data; get more */
					mFrameSize = audio_decode_frame(mFrame);
					if (mFrameSize < 0)
					{
						/* If error, we're done */
						break;
					}

					mFramePos = std::min<ssize_t>(mFrameSize, sample_skip);
					sample_skip -= mFramePos;
					continue;
				}

				size_t len1 = len - total;
				if (mFramePos >= 0)
				{
					len1 = std::min<size_t>(len1, mFrameSize - mFramePos);
					memcpy(stream, mFrame->data[0] + mFramePos, len1);
				}
				else
				{
					len1 = std::min<size_t>(len1, -mFramePos);

					int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) *
						mAVStream->codec->channels;

					/* add samples by copying the first sample*/
					if (n == 1)
						memset(stream, *mFrame->data[0], len1);
					else if (n == 2)
					{
						const int16_t val = *((int16_t*) mFrame->data[0]);
						for (size_t nb = 0; nb < len1; nb += n)
							*((int16_t*) (stream + nb)) = val;
					}
					else if (n == 4)
					{
						const int32_t val = *((int32_t*) mFrame->data[0]);
						for (size_t nb = 0; nb < len1; nb += n)
							*((int32_t*) (stream + nb)) = val;
					}
					else if (n == 8)
					{
						const int64_t val = *((int64_t*) mFrame->data[0]);
						for (size_t nb = 0; nb < len1; nb += n)
							*((int64_t*) (stream + nb)) = val;
					}
					else
					{
						for (size_t nb = 0; nb < len1; nb += n)
							memcpy(stream + nb, mFrame->data[0], n);
					}
				}

				total += len1;
				stream += len1;
				mFramePos += len1;
			}

			return total;
		}

		size_t getSampleOffset()
		{
			ssize_t clock_delay = (mFrameSize - mFramePos) / mAVStream->codec->channels /
				av_get_bytes_per_sample(mAVStream->codec->sample_fmt);
			return (size_t) (mAudioClock*mAVStream->codec->sample_rate) - clock_delay;
		}
	};


	int VideoState::OgreResource_Read(void *user_data, uint8_t *buf, int buf_size)
	{
		Ogre::DataStreamPtr stream = static_cast<VideoState*>(user_data)->stream;
		return stream->read(buf, buf_size);
	}

	int VideoState::OgreResource_Write(void *user_data, uint8_t *buf, int buf_size)
	{
		Ogre::DataStreamPtr stream = static_cast<VideoState*>(user_data)->stream;
		return stream->write(buf, buf_size);
	}

	int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whence)
	{
		Ogre::DataStreamPtr stream = static_cast<VideoState*>(user_data)->stream;

		whence &= ~AVSEEK_FORCE;
		if (whence == AVSEEK_SIZE)
			return stream->size();
		if (whence == SEEK_SET)
			stream->seek(offset);
		else if (whence == SEEK_CUR)
			stream->seek(stream->tell() + offset);
		else if (whence == SEEK_END)
			stream->seek(stream->size() + offset);
		else
			return -1;

		return stream->tell();
	}


	void VideoState::video_refresh(VideoState* is)
	{
		auto t = std::chrono::system_clock().now();
		while (!is->quit)
		{
			t += std::chrono::milliseconds(is->refresh_rate_ms);
			std::this_thread::sleep_until(t);
			is->refresh = true;
		}
	}


	void VideoState::video_display()
	{
		VideoPicture *vp = &this->pictq[this->pictq_rindex];

		if ((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0)
		{
			Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture");
			if (texture.isNull() || static_cast<int>(texture->getWidth()) != (*this->video_st)->codec->width
				|| static_cast<int>(texture->getHeight()) != (*this->video_st)->codec->height)
			{
				Ogre::TextureManager::getSingleton().remove("VideoTexture");
				texture = Ogre::TextureManager::getSingleton().createManual(
					"VideoTexture",
					Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
					Ogre::TEX_TYPE_2D,
					(*this->video_st)->codec->width, (*this->video_st)->codec->height,
					0,
					Ogre::PF_BYTE_RGBA,
					Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
			}
			Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]);
			Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer();
			buffer->blitFromMemory(pb);
			this->display_ready = true;
		}
	}

	void VideoState::video_refresh_timer()
	{
		VideoPicture *vp;
		float delay;

		if (this->pictq_size == 0)
			return;

		vp = &this->pictq[this->pictq_rindex];

		delay = vp->pts - this->frame_last_pts; /* the pts from last time */
		if (delay <= 0.0f || delay >= 1.0f) {
			/* if incorrect delay, use previous one */
			delay = this->frame_last_delay;
		}
		/* save for next time */
		this->frame_last_delay = delay;
		this->frame_last_pts = vp->pts;

		/* FIXME: Syncing should be done in the decoding stage, where frames can be
		* skipped or duplicated as needed. */
		/* update delay to sync to audio if not master source */
		if (this->av_sync_type != AV_SYNC_VIDEO_MASTER)
		{
			float diff = this->get_video_clock() - this->get_master_clock();

			/* Skip or repeat the frame. Take delay into account
			* FFPlay still doesn't "know if this is the best guess." */
			float sync_threshold = std::max(delay, AV_SYNC_THRESHOLD);
			if (diff <= -sync_threshold)
				delay = 0;
			else if (diff >= sync_threshold)
				delay = 2 * delay;
		}

		this->refresh_rate_ms = std::max<int>(1, (int) (delay*1000.0f));
		/* show the picture! */
		this->video_display();

		/* update queue for next picture! */
		this->pictq_rindex = (this->pictq_rindex + 1) % VIDEO_PICTURE_QUEUE_SIZE;
		this->pictq_mutex.lock();
		this->pictq_size--;
		this->pictq_cond.notify_one();
		this->pictq_mutex.unlock();
	}


	int VideoState::queue_picture(AVFrame *pFrame, float pts)
	{
		VideoPicture *vp;

		/* wait until we have a new pic */
		{
			std::unique_lock<std::mutex> lock(this->pictq_mutex);
			while (this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->quit)
				this->pictq_cond.wait_for(lock, std::chrono::milliseconds(1));
		}
		if (this->quit)
			return -1;

		// windex is set to 0 initially
		vp = &this->pictq[this->pictq_windex];

		// Convert the image into RGBA format for Ogre
		if (this->sws_context == NULL)
		{
			int w = (*this->video_st)->codec->width;
			int h = (*this->video_st)->codec->height;
			this->sws_context = sws_getContext(w, h, (*this->video_st)->codec->pix_fmt,
				w, h, PIX_FMT_RGBA, SWS_BICUBIC,
				NULL, NULL, NULL);
			if (this->sws_context == NULL)
				Error("[VideoPlayer]Cannot initialize the conversion context!\n");
		}

		vp->pts = pts;
		vp->data.resize((*this->video_st)->codec->width * (*this->video_st)->codec->height * 4);

		uint8_t *dst = &vp->data[0];

		sws_scale(this->sws_context, pFrame->data, pFrame->linesize,
			0, (*this->video_st)->codec->height, &dst, this->rgbaFrame->linesize);

		// now we inform our display thread that we have a pic ready
		this->pictq_windex = (this->pictq_windex + 1) % VIDEO_PICTURE_QUEUE_SIZE;
		this->pictq_mutex.lock();
		this->pictq_size++;
		this->pictq_mutex.unlock();

		return 0;
	}

	float VideoState::synchronize_video(AVFrame *src_frame, float pts)
	{
		float frame_delay;

		/* if we have pts, set video clock to it */
		if (pts != 0)
			this->video_clock = pts;
		else
			pts = this->video_clock;

		/* update the video clock */
		frame_delay = av_q2d((*this->video_st)->codec->time_base);

		/* if we are repeating a frame, adjust clock accordingly */
		frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
		this->video_clock += frame_delay;

		return pts;
	}


	/* These are called whenever we allocate a frame
	* buffer. We use this to store the global_pts in
	* a frame at the time it is allocated.
	*/
	static uint64_t global_video_pkt_pts = AV_NOPTS_VALUE;
	static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic)
	{
		int ret = avcodec_default_get_buffer(c, pic);
		uint64_t *pts = (uint64_t*) av_malloc(sizeof(uint64_t));
		*pts = global_video_pkt_pts;
		pic->opaque = pts;
		return ret;
	}
	static void our_release_buffer(struct AVCodecContext *c, AVFrame *pic)
	{
		if (pic) av_freep(&pic->opaque);
		avcodec_default_release_buffer(c, pic);
	}


	void VideoState::video_thread_loop(VideoState *self)
	{
		AVPacket pkt1, *packet = &pkt1;
		int frameFinished;
		AVFrame *pFrame;
		float pts;

		pFrame = avcodec_alloc_frame();

		self->rgbaFrame = avcodec_alloc_frame();
		avpicture_alloc((AVPicture*) self->rgbaFrame, PIX_FMT_RGBA, (*self->video_st)->codec->width, (*self->video_st)->codec->height);

		while (self->videoq.get(packet, self) >= 0)
		{
			// Save global pts to be stored in pFrame
			global_video_pkt_pts = packet->pts;
			// Decode video frame
			if (avcodec_decode_video2((*self->video_st)->codec, pFrame, &frameFinished, packet) < 0)
				Error("[VideoPlayer]Error decoding video frame");

			pts = 0;
			if ((uint64_t) packet->dts != AV_NOPTS_VALUE)
				pts = packet->dts;
			else if (pFrame->opaque && *(uint64_t*) pFrame->opaque != AV_NOPTS_VALUE)
				pts = *(uint64_t*) pFrame->opaque;
			pts *= av_q2d((*self->video_st)->time_base);

			av_free_packet(packet);

			// Did we get a video frame?
			if (frameFinished)
			{
				pts = self->synchronize_video(pFrame, pts);
				if (self->queue_picture(pFrame, pts) < 0)
					break;
			}
		}

		av_free(pFrame);

		avpicture_free((AVPicture*) self->rgbaFrame);
		av_free(self->rgbaFrame);
	}

	void VideoState::decode_thread_loop(VideoState *self)
	{
		AVFormatContext *pFormatCtx = self->format_ctx;
		AVPacket pkt1, *packet = &pkt1;

		try
		{
			if (!self->video_st && !self->audio_st)
				Error("[VideoPlayer]No streams to decode");

			// main decode loop
			while (!self->quit)
			{
				if ((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) ||
					(self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE))
				{
					std::this_thread::sleep_for(std::chrono::milliseconds(10));
					continue;
				}

				if (av_read_frame(pFormatCtx, packet) < 0)
					break;

				// Is this a packet from the video stream?
				if (self->video_st && packet->stream_index == self->video_st - pFormatCtx->streams)
					self->videoq.put(packet);
				else if (self->audio_st && packet->stream_index == self->audio_st - pFormatCtx->streams)
					self->audioq.put(packet);
				else
					av_free_packet(packet);
			}

			/* all done - wait for it */
			self->videoq.flush();
			self->audioq.flush();

			while (!self->quit)
			{
				// EOF reached, all packets processed, we can exit now
				if (self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0)
					break;
				 std::this_thread::sleep_for(std::chrono::milliseconds(100));
			}
		}
		catch (std::runtime_error& e) {
			Warning("[VideoPlayer]An error occured playing the video: %s", e.what());
		}
		catch (Ogre::Exception& e){
			Warning("[VideoPlayer]An error occured playing the video: %s", e.getFullDescription());
		}
		self->quit = true;
	}


	bool VideoState::update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height)
	{
		if (this->quit)
			return false;

		if (this->refresh)
		{
			this->refresh = false;
			this->video_refresh_timer();
			// Would be nice not to do this all the time...
			if (this->display_ready)
				mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture");

			// Correct aspect ratio by adding black bars
			float videoaspect = av_q2d((*this->video_st)->codec->sample_aspect_ratio);
			if (videoaspect == 0.0f)
				videoaspect = 1.0f;
			videoaspect *= static_cast<float>((*this->video_st)->codec->width) / (*this->video_st)->codec->height;

			float screenaspect = static_cast<float>(screen_width) / screen_height;
			float aspect_correction = videoaspect / screenaspect;

			rect->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min(1.0, 1.0 / aspect_correction),
				std::min(1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction));
		}
		return true;
	}


	int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx)
	{
		//MWSound::DecoderPtr decoder;
		AVCodecContext *codecCtx;
		AVCodec *codec;

		if (stream_index < 0 || stream_index >= static_cast<int>(pFormatCtx->nb_streams))
			return -1;

		// Get a pointer to the codec context for the video stream
		codecCtx = pFormatCtx->streams[stream_index]->codec;
		codec = avcodec_find_decoder(codecCtx->codec_id);
		if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0))
		{
			fprintf(stderr, "Unsupported codec!\n");
			return -1;
		}

		switch (codecCtx->codec_type)
		{
#if 0
		case AVMEDIA_TYPE_AUDIO:
			this->audio_st = pFormatCtx->streams + stream_index;

			decoder.reset(new MovieAudioDecoder(this));
			this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder, MWBase::SoundManager::Play_TypeMovie);
			if(!this->AudioTrack)
			{
				avcodec_close((*this->audio_st)->codec);
				this->audio_st = NULL;
				return -1;
			}
			break;
#endif
		case AVMEDIA_TYPE_VIDEO:
			this->video_st = pFormatCtx->streams + stream_index;

			this->frame_last_delay = 40e-3;

			codecCtx->get_buffer = our_get_buffer;
			codecCtx->release_buffer = our_release_buffer;
			this->video_thread = std::thread(video_thread_loop, this);
			this->refresh_thread = std::thread(video_refresh, this);
			break;

		default:
			break;
		}

		return 0;
	}

	void VideoState::init(const std::string& resourceName)
	{
		int video_index = -1;
		int audio_index = -1;
		unsigned int i;

		this->av_sync_type = AV_SYNC_DEFAULT;
		this->refresh_rate_ms = 10;
		this->refresh = false;
		this->quit = false;

		this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName);
		if (this->stream.isNull())
			Error("[VideoPlayer]Failed to open video resource");

		AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek);
		if (!ioCtx) Error("[VideoPlayer]Failed to allocate AVIOContext");

		this->format_ctx = avformat_alloc_context();
		if (this->format_ctx)
			this->format_ctx->pb = ioCtx;

		// Open video file
		/// \todo leak here, ffmpeg or valgrind bug ?
		if (!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL))
		{
			// "Note that a user-supplied AVFormatContext will be freed on failure."
			this->format_ctx = NULL;
			av_free(ioCtx);
			Error("[VideoPlayer]Failed to open video input");
		}

		// Retrieve stream information
		if (avformat_find_stream_info(this->format_ctx, NULL) < 0)
			Error("[VideoPlayer]Failed to retrieve stream information");

		// Dump information about file onto standard error
		av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0);

		for (i = 0; i < this->format_ctx->nb_streams; i++)
		{
			if (this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0)
				video_index = i;
			if (this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0)
				audio_index = i;
		}

		this->external_clock_base = av_gettime();
		if (audio_index >= 0)
			this->stream_open(audio_index, this->format_ctx);
		if (video_index >= 0)
			this->stream_open(video_index, this->format_ctx);

		this->parse_thread = std::thread(decode_thread_loop, this);
	}

	void VideoState::deinit()
	{
		this->quit = true;

		this->audioq.cond.notify_one();
		this->videoq.cond.notify_one();

		this->parse_thread.join();
		this->video_thread.join()[b];//!!!CrASH[/b]
		this->refresh_thread.join();

		if (this->audio_st)
			avcodec_close((*this->audio_st)->codec);
		this->audio_st = NULL;
		if (this->video_st)
			avcodec_close((*this->video_st)->codec);
		this->video_st = NULL;

		if (this->sws_context)
			sws_freeContext(this->sws_context);
		this->sws_context = NULL;

		if (this->format_ctx)
		{
			AVIOContext *ioContext = this->format_ctx->pb;
			avformat_close_input(&this->format_ctx);
			av_free(ioContext);
		}
	}

#else // defined COMPONENT_USE_FFMPEG

	class VideoState
	{
	public:
		VideoState() { }

		void init(const std::string& resourceName)
		{
			throw Error("[VideoPlayer]FFmpeg not supported, cannot play \""+resourceName+"\"");
		}
		void deinit() { }

		void close() { }

		bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height)
		{ return false; }
	};

#endif // defined COMPONENT_USE_FFMPEG


	VideoPlayer::VideoPlayer(Render* _render)
		: mState(NULL)
		, mSceneMgr(_render->mSceneMgr)
		, mVideoMaterial(NULL)
		, mRectangle(NULL)
		, mNode(NULL)
		, mAllowSkipping(false)
		, mWidth(800)//Nick
		, mHeight(600)//Nick
#pragma message("[Nick]TODO:сделай размеры плеера")
	{
		mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General");
		if (mVideoMaterial.isNull())
		{
			mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General");
			mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
			mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
			mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
			mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState();
			mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
		}
		mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png");

		Ogre::MaterialPtr blackMaterial = Ogre::MaterialManager::getSingleton().getByName("BlackBarsMaterial", "General");
		if (blackMaterial.isNull())
		{
			blackMaterial = Ogre::MaterialManager::getSingleton().create("BlackBarsMaterial", "General");
			blackMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
			blackMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
			blackMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
			blackMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png");
		}

		mRectangle = new Ogre::Rectangle2D(true);
		mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0);
		mRectangle->setMaterial("VideoMaterial");
		//Nick   
		mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY + 2);
		mBackgroundRectangle = new Ogre::Rectangle2D(true);
		mBackgroundRectangle->setCorners(-1.0, 1.0, 1.0, -1.0);
		mBackgroundRectangle->setMaterial("BlackBarsMaterial");
		//Nick   
		mBackgroundRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY + 1);

		// Use infinite AAB to always stay visible
		Ogre::AxisAlignedBox aabInf;
		aabInf.setInfinite();
		mRectangle->setBoundingBox(aabInf);
		mBackgroundRectangle->setBoundingBox(aabInf);

		// Attach background to the scene
		mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		mNode->attachObject(mRectangle);
		mBackgroundNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		mBackgroundNode->attachObject(mBackgroundRectangle);

		mRectangle->setVisible(false);
		//Nick
		mRectangle->setVisibilityFlags(1024);
		mBackgroundRectangle->setVisible(false);
		//Nick
		mBackgroundRectangle->setVisibilityFlags(1024);
	}

	VideoPlayer::~VideoPlayer()
	{
		if (mState)
			close();

		mSceneMgr->destroySceneNode(mNode);
		mSceneMgr->destroySceneNode(mBackgroundNode);

		delete mRectangle;
		delete mBackgroundRectangle;
	}

	void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping)
	{
		mAllowSkipping = allowSkipping;

		if (mState)
			close();

		mRectangle->setVisible(true);
		mBackgroundRectangle->setVisible(true);
		mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png");

		//Nick   MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video);

		// Turn off rendering except the GUI
		mSceneMgr->clearSpecialCaseRenderQueues();


		// SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work.
		for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i)
		{
			if (i > 0 && i < 96)
				mSceneMgr->addSpecialCaseRenderQueue(i);
		}
		mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE);

		//Nick   MWBase::Environment::get().getSoundManager()->pauseSounds();

		try {
			mState = new VideoState;
			mState->init(resourceName);
		}
		catch (std::exception& e) {
			Warning("[VideoPlayer]Failed to play video: %s", e.what());
			close();
		}
	}

	void VideoPlayer::Update()
	{
		if (mState)
		{
			if (!mState->update(mVideoMaterial, mRectangle, mWidth, mHeight))
				close();
		}
	}

	void VideoPlayer::stopVideo()
	{
		if (mAllowSkipping)
			close();
	}

	void VideoPlayer::close()
	{
		if (mState)
		{
			mState->deinit();

			delete mState;
			mState = NULL;
		}

		//Nick   MWBase::Environment::get().getSoundManager()->resumeSounds();

		mRectangle->setVisible(false);
		mBackgroundRectangle->setVisible(false);
		//Nick   MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video);

		mSceneMgr->clearSpecialCaseRenderQueues();
		mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE);
	}

	bool VideoPlayer::isPlaying()
	{
		return mState != NULL;
	}


}
Image
rlamostre0
Gnoblar
Posts: 1
Joined: Wed Sep 18, 2013 2:11 am

Re: FFMPEG video player - OGRE window, SDL audio

Post by rlamostre0 »

nice, i've been looking for this since theora plugin didn't work for me. the problem that i'm currently stuck now is the boost dependency. should i download the latest boost or should i stick with ogre's boost? the current version i'm using is ogre 1.9. any help will be much appreciated.
Attachments
screen.png
screen.png (32.45 KiB) Viewed 10278 times
Forcecast
Gnoblar
Posts: 14
Joined: Thu Apr 18, 2013 4:32 pm

Re: FFMPEG video player - OGRE window, SDL audio

Post by Forcecast »

Interesting approach, I managed to make a working version using the improved VideoPlayer.
I've quickly looked at the code, the separate decoding thread is nice but also noticed that it doesn't include video looping.

I'm keeping my eyes on this one ;p
dudeabot
Gnome
Posts: 334
Joined: Thu Jun 28, 2007 2:12 pm
Location: Brazil
x 5
Contact:

Re: FFMPEG video player - OGRE window, SDL audio

Post by dudeabot »

hello, nice project!

i get a black screen tough, i have tried two differents videos

here is the console output from one:
Input #0, flv, from 'movie.avi':
Metadata:
hasAudio : false
metadatacreator : inlet media FLVTool2 v1.0.6 - http://www.inlet-media.de/fl
vtool2
hasMetadata : true
hasVideo : true
lasttimestamp : 1194
videosize : 31960030
hasKeyframes : true
canSeekToEnd : false
audiosize : 0
hasCuePoints : false
audiodelay : 0
lastkeyframetimestamp: 1192
datasize : 31966521
Duration: 00:19:54.40, start: 0.000000, bitrate: 214 kb/s
Stream #0:0: Video: flv1, yuv420p, 480x400, 218 kb/s, 15 tbr, 1k tbn, 1k tbc
compiled with SDL 1.2, and ogre SDK 1.9
nickG
Greenskin
Posts: 122
Joined: Fri Jan 20, 2012 6:44 pm
Location: Russia,Moscow
x 1

Re: FFMPEG video player - OGRE window, SDL audio

Post by nickG »

dudeabot wrote:hello, nice project!
i get a black screen tough, i have tried two differents videos
i has got similiar problem in my engine,when AA(SMAA) was enabled.

Fix for me:
1)disable AA if video playing
2)Restore State of AA
scrawl
OGRE Expert User
OGRE Expert User
Posts: 1119
Joined: Sat Jan 01, 2011 7:57 pm
x 216

Re: FFMPEG video player - OGRE window, SDL audio

Post by scrawl »

Hi guys,

I've created a heavily updated version of the player with lots of new features - check the new thread: http://www.ogre3d.org/forums/viewtopic.php?f=5&t=81934
Post Reply