mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 00:23:51 +00:00
more clean up, video played with correct speed, videos without sound working too (mw_credits.bik)
This commit is contained in:
parent
a77d910aaf
commit
bc90c75176
2 changed files with 228 additions and 132 deletions
|
@ -19,6 +19,9 @@ extern "C"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
#define MIN_QUEUED_PACKETS 30
|
||||||
|
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -69,10 +72,76 @@ namespace MWRender
|
||||||
return stream->tell();
|
return stream->tell();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
AVPacketQueue::AVPacketQueue():
|
||||||
|
mFirstPacket(NULL), mLastPacket(NULL), mNumPackets(0), mSize(0)
|
||||||
|
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int AVPacketQueue::put(AVPacket* pkt)
|
||||||
|
{
|
||||||
|
if(av_dup_packet(pkt) < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVPacketList* pkt1;
|
||||||
|
pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList));
|
||||||
|
if (pkt1 == NULL) return -1;
|
||||||
|
pkt1->pkt = *pkt;
|
||||||
|
pkt1->next = NULL;
|
||||||
|
|
||||||
|
if (mLastPacket == NULL) mFirstPacket = pkt1;
|
||||||
|
else mLastPacket->next = pkt1;
|
||||||
|
|
||||||
|
mLastPacket = pkt1;
|
||||||
|
mNumPackets++;
|
||||||
|
mSize += pkt1->pkt.size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AVPacketQueue::get(AVPacket* pkt, int block)
|
||||||
|
{
|
||||||
|
AVPacketList* pkt1;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
pkt1 = mFirstPacket;
|
||||||
|
if (pkt1 != NULL)
|
||||||
|
{
|
||||||
|
mFirstPacket = pkt1->next;
|
||||||
|
|
||||||
|
if (mFirstPacket == NULL) mLastPacket = NULL;
|
||||||
|
|
||||||
|
mNumPackets--;
|
||||||
|
mSize -= pkt1->pkt.size;
|
||||||
|
*pkt = pkt1->pkt;
|
||||||
|
av_free(pkt1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (block == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr)
|
VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr)
|
||||||
: mAvContext(NULL)
|
: mAvContext(NULL)
|
||||||
, mVideoStreamId(-1)
|
, mVideoStreamId(-1)
|
||||||
, mAudioStreamId(-1)
|
, mAudioStreamId(-1)
|
||||||
|
, mVideoClock(0)
|
||||||
|
, mAudioClock(0)
|
||||||
|
, mClock(0)
|
||||||
{
|
{
|
||||||
Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General");
|
Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General");
|
||||||
videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
|
videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
|
||||||
|
@ -110,8 +179,11 @@ namespace MWRender
|
||||||
|
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
mAudioStreamId = -1;
|
mAudioStreamId = -1;
|
||||||
mEOF = false;
|
mAudioStream = NULL;
|
||||||
mDisplayedFrameCount = 0;
|
mVideoStream = NULL;
|
||||||
|
mVideoClock = 0;
|
||||||
|
mAudioClock = 0;
|
||||||
|
mClock = 0;
|
||||||
|
|
||||||
// if something is already playing, close it
|
// if something is already playing, close it
|
||||||
if (mAvContext)
|
if (mAvContext)
|
||||||
|
@ -189,20 +261,20 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-1 == mAudioStreamId)
|
if (mAudioStreamId >= 0)
|
||||||
throw std::runtime_error("No audio stream found in the video");
|
|
||||||
|
|
||||||
// Get the audio decoder
|
|
||||||
mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id);
|
|
||||||
if (mAudioCodec == NULL)
|
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Stream doesn't have an audio codec");
|
// Get the audio decoder
|
||||||
}
|
mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id);
|
||||||
|
if (mAudioCodec == NULL)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Stream doesn't have an audio codec");
|
||||||
|
}
|
||||||
|
|
||||||
// Load the audio codec
|
// Load the audio codec
|
||||||
err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0);
|
err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
throwError (err);
|
throwError (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -229,24 +301,6 @@ namespace MWRender
|
||||||
if (!mSwsContext)
|
if (!mSwsContext)
|
||||||
throw std::runtime_error("Can't create SWS Context");
|
throw std::runtime_error("Can't create SWS Context");
|
||||||
|
|
||||||
// Get the frame time we need for this video
|
|
||||||
AVRational r = mVideoStream->avg_frame_rate;
|
|
||||||
AVRational r2 = mVideoStream->r_frame_rate;
|
|
||||||
if ((!r.num || !r.den) &&
|
|
||||||
(!r2.num || !r2.den))
|
|
||||||
{
|
|
||||||
std::cerr << "Warning - unable to get the video frame rate. Using standard NTSC frame rate : 29.97 fps." << std::endl;
|
|
||||||
mWantedFrameTime = 1.f / 29.97f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (r.num && r.den)
|
|
||||||
mWantedFrameTime = 1.f/((float)r.num / r.den);
|
|
||||||
else
|
|
||||||
mWantedFrameTime = 1.f/((float)r2.num / r2.den);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mTextureUnit->setTextureName ("");
|
mTextureUnit->setTextureName ("");
|
||||||
|
|
||||||
if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull())
|
if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull())
|
||||||
|
@ -273,8 +327,23 @@ namespace MWRender
|
||||||
|
|
||||||
mTextureUnit->setTextureName ("VideoTexture");
|
mTextureUnit->setTextureName ("VideoTexture");
|
||||||
|
|
||||||
mTimer.reset();
|
|
||||||
|
|
||||||
|
// Queue up some packets
|
||||||
|
while(
|
||||||
|
mVideoPacketQueue.getNumPackets()<MIN_QUEUED_PACKETS ||
|
||||||
|
(mAudioStream && mAudioPacketQueue.getNumPackets()<MIN_QUEUED_PACKETS)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//Keep adding until we have 30 video and audio packets.
|
||||||
|
bool addedPacket = addToBuffer();
|
||||||
|
if(!addedPacket)
|
||||||
|
{
|
||||||
|
//No more packets
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mTimer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayer::throwError(int error)
|
void VideoPlayer::throwError(int error)
|
||||||
|
@ -292,103 +361,67 @@ namespace MWRender
|
||||||
throw std::runtime_error("Unknown FFMPEG error");
|
throw std::runtime_error("Unknown FFMPEG error");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoPlayer::readFrameAndQueue ()
|
|
||||||
{
|
|
||||||
bool ret = true;
|
|
||||||
AVPacket *pkt = NULL;
|
|
||||||
|
|
||||||
// check we're not at eof
|
|
||||||
if (mEOF)
|
|
||||||
ret = false;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// read frame
|
|
||||||
pkt = (AVPacket *)av_malloc(sizeof(*pkt));
|
|
||||||
int res = av_read_frame(mAvContext, pkt);
|
|
||||||
|
|
||||||
// check we didn't reach eof right now
|
|
||||||
if (res < 0)
|
|
||||||
{
|
|
||||||
mEOF = true;
|
|
||||||
ret = false;
|
|
||||||
av_free(pkt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// When a frame has been read, save it
|
|
||||||
if (!saveFrame(pkt))
|
|
||||||
{
|
|
||||||
// we failed to save it, which means it was unknown type of frame - delete it here
|
|
||||||
av_free_packet(pkt);
|
|
||||||
av_free(pkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VideoPlayer::saveFrame(AVPacket* frame)
|
|
||||||
{
|
|
||||||
bool saved = false;
|
|
||||||
|
|
||||||
if (frame->stream_index == mVideoStreamId)
|
|
||||||
{
|
|
||||||
// If it was a video frame...
|
|
||||||
mVideoPacketQueue.push(frame);
|
|
||||||
saved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return saved;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoPlayer::update()
|
void VideoPlayer::update()
|
||||||
{
|
{
|
||||||
if (!mAvContext)
|
if (!mAvContext)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Time elapsed since the video started
|
double dt = mTimer.getMilliseconds () / 1000.f;
|
||||||
float realTime = mTimer.getMilliseconds ()/1000.f;
|
mTimer.reset ();
|
||||||
|
|
||||||
// Here is the time we're at in the video
|
//UpdateAudio(fTime);
|
||||||
float movieTime = mDisplayedFrameCount * mWantedFrameTime;
|
std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl;
|
||||||
|
while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock)
|
||||||
if (movieTime >= realTime)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!mVideoPacketQueue.size() && mEOF)
|
|
||||||
close();
|
|
||||||
|
|
||||||
Ogre::Timer timer;
|
|
||||||
|
|
||||||
if (!mVideoPacketQueue.size())
|
|
||||||
{
|
{
|
||||||
if (readFrameAndQueue())
|
while(
|
||||||
decodeFrontFrame();
|
mVideoPacketQueue.getNumPackets()<MIN_QUEUED_PACKETS ||
|
||||||
}
|
(mAudioStream && mAudioPacketQueue.getNumPackets()<MIN_QUEUED_PACKETS)
|
||||||
else
|
)
|
||||||
decodeFrontFrame();
|
{
|
||||||
|
//Keep adding until we have 30 video and audio packets.
|
||||||
|
bool addedPacket = addToBuffer();
|
||||||
|
|
||||||
mDecodingTime = timer.getMilliseconds ()/1000.f;
|
if(!addedPacket)
|
||||||
|
{
|
||||||
|
//No more packets
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVideoPacketQueue.getNumPackets ())
|
||||||
|
decodeNextVideoFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
mClock += dt;
|
||||||
|
|
||||||
|
//curTime += fTime;
|
||||||
|
|
||||||
|
if(mVideoPacketQueue.getNumPackets()==0 /* && mAudioPacketQueue.getNumPackets()==0 */)
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayer::decodeFrontFrame ()
|
void VideoPlayer::decodeNextVideoFrame ()
|
||||||
{
|
{
|
||||||
int didDecodeFrame = 0;
|
|
||||||
|
|
||||||
// Make sure there is something to decode
|
// Make sure there is something to decode
|
||||||
if (!mVideoPacketQueue.size())
|
assert (mVideoPacketQueue.getNumPackets ());
|
||||||
return;
|
|
||||||
|
|
||||||
// Get the front frame and decode it
|
// Get the front frame and decode it
|
||||||
AVPacket *videoPacket = mVideoPacketQueue.front();
|
AVPacket packet;
|
||||||
|
mVideoPacketQueue.get(&packet, 1);
|
||||||
|
|
||||||
int res;
|
int res;
|
||||||
res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, videoPacket);
|
int didDecodeFrame = 0;
|
||||||
|
res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, &packet);
|
||||||
|
|
||||||
if (res < 0 || !didDecodeFrame)
|
if (res < 0 || !didDecodeFrame)
|
||||||
throw std::runtime_error ("an error occured while decoding the video frame");
|
throw std::runtime_error ("an error occured while decoding the video frame");
|
||||||
|
|
||||||
|
// Set video clock to the PTS of this packet (presentation timestamp)
|
||||||
|
double pts = 0;
|
||||||
|
if (packet.pts != -1.0) pts = packet.pts;
|
||||||
|
pts *= av_q2d(mVideoStream->time_base);
|
||||||
|
mVideoClock = pts;
|
||||||
|
|
||||||
// Convert the frame to RGB
|
// Convert the frame to RGB
|
||||||
sws_scale(mSwsContext,
|
sws_scale(mSwsContext,
|
||||||
mRawFrame->data, mRawFrame->linesize,
|
mRawFrame->data, mRawFrame->linesize,
|
||||||
|
@ -400,11 +433,7 @@ namespace MWRender
|
||||||
Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]);
|
Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]);
|
||||||
pixelBuffer->blitFromMemory(pb);
|
pixelBuffer->blitFromMemory(pb);
|
||||||
|
|
||||||
av_free_packet(mVideoPacketQueue.front());
|
if (packet.data != NULL) av_free_packet(&packet);
|
||||||
av_free(mVideoPacketQueue.front());
|
|
||||||
mVideoPacketQueue.pop();
|
|
||||||
|
|
||||||
++mDisplayedFrameCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayer::close ()
|
void VideoPlayer::close ()
|
||||||
|
@ -416,11 +445,17 @@ namespace MWRender
|
||||||
|
|
||||||
void VideoPlayer::deleteContext()
|
void VideoPlayer::deleteContext()
|
||||||
{
|
{
|
||||||
while (mVideoPacketQueue.size())
|
while (mVideoPacketQueue.getNumPackets ())
|
||||||
{
|
{
|
||||||
av_free_packet(mVideoPacketQueue.front());
|
AVPacket packet;
|
||||||
av_free(mVideoPacketQueue.front());
|
mVideoPacketQueue.get(&packet, 1);
|
||||||
mVideoPacketQueue.pop();
|
if (packet.data != NULL) av_free_packet(&packet);
|
||||||
|
}
|
||||||
|
while (mAudioPacketQueue.getNumPackets ())
|
||||||
|
{
|
||||||
|
AVPacket packet;
|
||||||
|
mAudioPacketQueue.get(&packet, 1);
|
||||||
|
if (packet.data != NULL) av_free_packet(&packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec);
|
if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec);
|
||||||
|
@ -439,6 +474,46 @@ namespace MWRender
|
||||||
|
|
||||||
mAvContext = NULL;
|
mAvContext = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool VideoPlayer::addToBuffer()
|
||||||
|
{
|
||||||
|
if(mAvContext)
|
||||||
|
{
|
||||||
|
AVPacket packet;
|
||||||
|
if (av_read_frame(mAvContext, &packet) >= 0)
|
||||||
|
{
|
||||||
|
if (packet.stream_index == mVideoStreamId)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if(curTime==0)
|
||||||
|
{
|
||||||
|
curTime = packet.dts;
|
||||||
|
curTime *= av_q2d(m_pVideoSt->time_base);
|
||||||
|
std::cout << "Initializing curtime to: " << curTime << std::endl;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
mVideoPacketQueue.put(&packet);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (packet.stream_index == mAudioStreamId && mAudioStream)
|
||||||
|
{
|
||||||
|
mAudioPacketQueue.put(&packet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
av_free_packet(&packet);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endif
|
//#endif
|
||||||
|
|
|
@ -25,10 +25,31 @@ struct AVStream;
|
||||||
struct AVFrame;
|
struct AVFrame;
|
||||||
struct SwsContext;
|
struct SwsContext;
|
||||||
struct AVPacket;
|
struct AVPacket;
|
||||||
|
struct AVPacketList;
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// A simple queue used to queue raw audio and video data.
|
||||||
|
class AVPacketQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AVPacketQueue();
|
||||||
|
int put(AVPacket* pkt);
|
||||||
|
int get(AVPacket* pkt, int block);
|
||||||
|
|
||||||
|
bool isEmpty() const { return mNumPackets == 0; }
|
||||||
|
int getNumPackets() const { return mNumPackets; }
|
||||||
|
int getSize() const { return mSize; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVPacketList* mFirstPacket;
|
||||||
|
AVPacketList* mLastPacket;
|
||||||
|
int mNumPackets;
|
||||||
|
int mSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class VideoPlayer
|
class VideoPlayer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -52,9 +73,6 @@ namespace MWRender
|
||||||
|
|
||||||
AVFormatContext* mAvContext;
|
AVFormatContext* mAvContext;
|
||||||
|
|
||||||
|
|
||||||
bool mEOF;
|
|
||||||
|
|
||||||
Ogre::Timer mTimer;
|
Ogre::Timer mTimer;
|
||||||
|
|
||||||
AVCodec* mVideoCodec;
|
AVCodec* mVideoCodec;
|
||||||
|
@ -68,24 +86,27 @@ namespace MWRender
|
||||||
AVFrame* mRawFrame;
|
AVFrame* mRawFrame;
|
||||||
AVFrame* mRGBAFrame;
|
AVFrame* mRGBAFrame;
|
||||||
SwsContext* mSwsContext;
|
SwsContext* mSwsContext;
|
||||||
float mWantedFrameTime;
|
|
||||||
float mDecodingTime;
|
|
||||||
std::queue <AVPacket *> mVideoPacketQueue;
|
|
||||||
|
|
||||||
|
double mClock;
|
||||||
|
double mVideoClock;
|
||||||
|
double mAudioClock;
|
||||||
|
|
||||||
int mDisplayedFrameCount;
|
AVPacketQueue mVideoPacketQueue;
|
||||||
|
AVPacketQueue mAudioPacketQueue;
|
||||||
|
|
||||||
|
|
||||||
bool readFrameAndQueue();
|
|
||||||
bool saveFrame(AVPacket* frame);
|
|
||||||
|
|
||||||
void decodeFrontFrame();
|
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
void deleteContext();
|
void deleteContext();
|
||||||
|
|
||||||
|
|
||||||
void throwError(int error);
|
void throwError(int error);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool addToBuffer(); ///< try to add the next audio or video packet into the queue.
|
||||||
|
|
||||||
|
void decodeNextVideoFrame(); ///< decode the next video frame in the queue and display it.
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue