mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
Merge pull request #8302 from Robyt3/Video-Refactoring
Video recorder: improve error handling and log messages, fix crashes, refactoring
This commit is contained in:
commit
2f22447d44
|
@ -3301,9 +3301,14 @@ void CClient::StartVideo(const char *pFilename, bool WithTimestamp)
|
||||||
Graphics()->WaitForIdle();
|
Graphics()->WaitForIdle();
|
||||||
// pause the sound device while creating the video instance
|
// pause the sound device while creating the video instance
|
||||||
Sound()->PauseAudioDevice();
|
Sound()->PauseAudioDevice();
|
||||||
new CVideo((CGraphics_Threaded *)m_pGraphics, Sound(), Storage(), Graphics()->ScreenWidth(), Graphics()->ScreenHeight(), aFilename);
|
new CVideo(Graphics(), Sound(), Storage(), Graphics()->ScreenWidth(), Graphics()->ScreenHeight(), aFilename);
|
||||||
Sound()->UnpauseAudioDevice();
|
Sound()->UnpauseAudioDevice();
|
||||||
IVideo::Current()->Start();
|
if(!IVideo::Current()->Start())
|
||||||
|
{
|
||||||
|
log_error("videorecorder", "Failed to start recording to '%s'", aFilename);
|
||||||
|
m_DemoPlayer.Stop("Failed to start video recording. See local console for details.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(m_DemoPlayer.Info()->m_Info.m_Paused)
|
if(m_DemoPlayer.Info()->m_Info.m_Paused)
|
||||||
{
|
{
|
||||||
IVideo::Current()->Pause(true);
|
IVideo::Current()->Pause(true);
|
||||||
|
|
|
@ -131,7 +131,9 @@ public:
|
||||||
void StopVoice(CVoiceHandle Voice) override REQUIRES(!m_SoundLock);
|
void StopVoice(CVoiceHandle Voice) override REQUIRES(!m_SoundLock);
|
||||||
bool IsPlaying(int SampleId) override REQUIRES(!m_SoundLock);
|
bool IsPlaying(int SampleId) override REQUIRES(!m_SoundLock);
|
||||||
|
|
||||||
|
int MixingRate() const override { return m_MixingRate; }
|
||||||
void Mix(short *pFinalOut, unsigned Frames) override REQUIRES(!m_SoundLock);
|
void Mix(short *pFinalOut, unsigned Frames) override REQUIRES(!m_SoundLock);
|
||||||
|
|
||||||
void PauseAudioDevice() override;
|
void PauseAudioDevice() override;
|
||||||
void UnpauseAudioDevice() override;
|
void UnpauseAudioDevice() override;
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,39 +15,38 @@ extern "C" {
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#define ALEN 2048
|
|
||||||
|
|
||||||
class CGraphics_Threaded;
|
class IGraphics;
|
||||||
class ISound;
|
class ISound;
|
||||||
class IStorage;
|
class IStorage;
|
||||||
|
|
||||||
extern CLock g_WriteLock;
|
extern CLock g_WriteLock;
|
||||||
|
|
||||||
// a wrapper around a single output AVStream
|
// a wrapper around a single output AVStream
|
||||||
struct OutputStream
|
class COutputStream
|
||||||
{
|
{
|
||||||
AVStream *pSt = nullptr;
|
public:
|
||||||
AVCodecContext *pEnc = nullptr;
|
AVStream *m_pStream = nullptr;
|
||||||
|
AVCodecContext *m_pCodecContext = nullptr;
|
||||||
|
|
||||||
/* pts of the next frame that will be generated */
|
/* pts of the next frame that will be generated */
|
||||||
int64_t NextPts = 0;
|
|
||||||
int64_t m_SamplesCount = 0;
|
int64_t m_SamplesCount = 0;
|
||||||
int64_t m_SamplesFrameCount = 0;
|
int64_t m_SamplesFrameCount = 0;
|
||||||
|
|
||||||
std::vector<AVFrame *> m_vpFrames;
|
std::vector<AVFrame *> m_vpFrames;
|
||||||
std::vector<AVFrame *> m_vpTmpFrames;
|
std::vector<AVFrame *> m_vpTmpFrames;
|
||||||
|
|
||||||
std::vector<struct SwsContext *> m_vpSwsCtxs;
|
std::vector<struct SwsContext *> m_vpSwsContexts;
|
||||||
std::vector<struct SwrContext *> m_vpSwrCtxs;
|
std::vector<struct SwrContext *> m_vpSwrContexts;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CVideo : public IVideo
|
class CVideo : public IVideo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CVideo(CGraphics_Threaded *pGraphics, ISound *pSound, IStorage *pStorage, int Width, int Height, const char *pName);
|
CVideo(IGraphics *pGraphics, ISound *pSound, IStorage *pStorage, int Width, int Height, const char *pName);
|
||||||
~CVideo();
|
~CVideo();
|
||||||
|
|
||||||
void Start() override REQUIRES(!g_WriteLock);
|
bool Start() override REQUIRES(!g_WriteLock);
|
||||||
void Stop() override;
|
void Stop() override;
|
||||||
void Pause(bool Pause) override;
|
void Pause(bool Pause) override;
|
||||||
bool IsRecording() override { return m_Recording; }
|
bool IsRecording() override { return m_Recording; }
|
||||||
|
@ -60,12 +59,12 @@ public:
|
||||||
|
|
||||||
static IVideo *Current() { return IVideo::ms_pCurrentVideo; }
|
static IVideo *Current() { return IVideo::ms_pCurrentVideo; }
|
||||||
|
|
||||||
static void Init() { av_log_set_level(AV_LOG_DEBUG); }
|
static void Init();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock);
|
void RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock);
|
||||||
void FillVideoFrame(size_t ThreadIndex) REQUIRES(!g_WriteLock);
|
void FillVideoFrame(size_t ThreadIndex) REQUIRES(!g_WriteLock);
|
||||||
void ReadRGBFromGL(size_t ThreadIndex);
|
void UpdateVideoBufferFromGraphics(size_t ThreadIndex);
|
||||||
|
|
||||||
void RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock);
|
void RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex) REQUIRES(!g_WriteLock);
|
||||||
void FillAudioFrame(size_t ThreadIndex);
|
void FillAudioFrame(size_t ThreadIndex);
|
||||||
|
@ -75,27 +74,26 @@ private:
|
||||||
AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height);
|
AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height);
|
||||||
AVFrame *AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples);
|
AVFrame *AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples);
|
||||||
|
|
||||||
void WriteFrame(OutputStream *pStream, size_t ThreadIndex) REQUIRES(g_WriteLock);
|
void WriteFrame(COutputStream *pStream, size_t ThreadIndex) REQUIRES(g_WriteLock);
|
||||||
void FinishFrames(OutputStream *pStream);
|
void FinishFrames(COutputStream *pStream);
|
||||||
void CloseStream(OutputStream *pStream);
|
void CloseStream(COutputStream *pStream);
|
||||||
|
|
||||||
bool AddStream(OutputStream *pStream, AVFormatContext *pOC, const AVCodec **ppCodec, enum AVCodecID CodecId) const;
|
bool AddStream(COutputStream *pStream, AVFormatContext *pFormatContext, const AVCodec **ppCodec, enum AVCodecID CodecId) const;
|
||||||
|
|
||||||
CGraphics_Threaded *m_pGraphics;
|
IGraphics *m_pGraphics;
|
||||||
IStorage *m_pStorage;
|
IStorage *m_pStorage;
|
||||||
ISound *m_pSound;
|
ISound *m_pSound;
|
||||||
|
|
||||||
int m_Width;
|
int m_Width;
|
||||||
int m_Height;
|
int m_Height;
|
||||||
char m_aName[256];
|
char m_aName[256];
|
||||||
//FILE *m_dbgfile;
|
uint64_t m_VideoFrameIndex = 0;
|
||||||
uint64_t m_VSeq = 0;
|
uint64_t m_AudioFrameIndex = 0;
|
||||||
uint64_t m_ASeq = 0;
|
|
||||||
uint64_t m_Vframe;
|
|
||||||
|
|
||||||
int m_FPS;
|
int m_FPS;
|
||||||
|
|
||||||
bool m_Started;
|
bool m_Started;
|
||||||
|
bool m_Stopped;
|
||||||
bool m_Recording;
|
bool m_Recording;
|
||||||
|
|
||||||
size_t m_VideoThreads = 2;
|
size_t m_VideoThreads = 2;
|
||||||
|
@ -103,8 +101,9 @@ private:
|
||||||
size_t m_AudioThreads = 2;
|
size_t m_AudioThreads = 2;
|
||||||
size_t m_CurAudioThreadIndex = 0;
|
size_t m_CurAudioThreadIndex = 0;
|
||||||
|
|
||||||
struct SVideoRecorderThread
|
class CVideoRecorderThread
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
std::thread m_Thread;
|
std::thread m_Thread;
|
||||||
std::mutex m_Mutex;
|
std::mutex m_Mutex;
|
||||||
std::condition_variable m_Cond;
|
std::condition_variable m_Cond;
|
||||||
|
@ -118,10 +117,11 @@ private:
|
||||||
uint64_t m_VideoFrameToFill = 0;
|
uint64_t m_VideoFrameToFill = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::unique_ptr<SVideoRecorderThread>> m_vVideoThreads;
|
std::vector<std::unique_ptr<CVideoRecorderThread>> m_vpVideoThreads;
|
||||||
|
|
||||||
struct SAudioRecorderThread
|
class CAudioRecorderThread
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
std::thread m_Thread;
|
std::thread m_Thread;
|
||||||
std::mutex m_Mutex;
|
std::mutex m_Mutex;
|
||||||
std::condition_variable m_Cond;
|
std::condition_variable m_Cond;
|
||||||
|
@ -136,22 +136,28 @@ private:
|
||||||
int64_t m_SampleCountStart = 0;
|
int64_t m_SampleCountStart = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::unique_ptr<SAudioRecorderThread>> m_vAudioThreads;
|
std::vector<std::unique_ptr<CAudioRecorderThread>> m_vpAudioThreads;
|
||||||
|
|
||||||
std::atomic<int32_t> m_ProcessingVideoFrame;
|
std::atomic<int32_t> m_ProcessingVideoFrame;
|
||||||
std::atomic<int32_t> m_ProcessingAudioFrame;
|
std::atomic<int32_t> m_ProcessingAudioFrame;
|
||||||
|
|
||||||
bool m_HasAudio;
|
bool m_HasAudio;
|
||||||
|
|
||||||
struct SVideoSoundBuffer
|
class CVideoBuffer
|
||||||
{
|
{
|
||||||
int16_t m_aBuffer[ALEN * 2];
|
public:
|
||||||
|
std::vector<uint8_t> m_vBuffer;
|
||||||
};
|
};
|
||||||
std::vector<SVideoSoundBuffer> m_vBuffer;
|
std::vector<CVideoBuffer> m_vVideoBuffers;
|
||||||
std::vector<std::vector<uint8_t>> m_vPixelHelper;
|
class CAudioBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int16_t m_aBuffer[4096];
|
||||||
|
};
|
||||||
|
std::vector<CAudioBuffer> m_vAudioBuffers;
|
||||||
|
|
||||||
OutputStream m_VideoStream;
|
COutputStream m_VideoStream;
|
||||||
OutputStream m_AudioStream;
|
COutputStream m_AudioStream;
|
||||||
|
|
||||||
const AVCodec *m_pVideoCodec;
|
const AVCodec *m_pVideoCodec;
|
||||||
const AVCodec *m_pAudioCodec;
|
const AVCodec *m_pAudioCodec;
|
||||||
|
|
|
@ -91,6 +91,7 @@ public:
|
||||||
virtual int SetPos(int WantedTick) = 0;
|
virtual int SetPos(int WantedTick) = 0;
|
||||||
virtual void Pause() = 0;
|
virtual void Pause() = 0;
|
||||||
virtual void Unpause() = 0;
|
virtual void Unpause() = 0;
|
||||||
|
virtual const char *ErrorMessage() const = 0;
|
||||||
virtual bool IsPlaying() const = 0;
|
virtual bool IsPlaying() const = 0;
|
||||||
virtual const CInfo *BaseInfo() const = 0;
|
virtual const CInfo *BaseInfo() const = 0;
|
||||||
virtual void GetDemoName(char *pBuffer, size_t BufferSize) const = 0;
|
virtual void GetDemoName(char *pBuffer, size_t BufferSize) const = 0;
|
||||||
|
|
|
@ -167,8 +167,8 @@ public:
|
||||||
const CInfo *BaseInfo() const override { return &m_Info.m_Info; }
|
const CInfo *BaseInfo() const override { return &m_Info.m_Info; }
|
||||||
void GetDemoName(char *pBuffer, size_t BufferSize) const override;
|
void GetDemoName(char *pBuffer, size_t BufferSize) const override;
|
||||||
bool GetDemoInfo(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo, IOHANDLE *pFile = nullptr, char *pErrorMessage = nullptr, size_t ErrorMessageSize = 0) const override;
|
bool GetDemoInfo(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo, IOHANDLE *pFile = nullptr, char *pErrorMessage = nullptr, size_t ErrorMessageSize = 0) const override;
|
||||||
const char *Filename() { return m_aFilename; }
|
const char *Filename() const { return m_aFilename; }
|
||||||
const char *ErrorMessage() { return m_aErrorMessage; }
|
const char *ErrorMessage() const override { return m_aErrorMessage; }
|
||||||
|
|
||||||
int Update(bool RealTime = true);
|
int Update(bool RealTime = true);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ class IVideo
|
||||||
public:
|
public:
|
||||||
virtual ~IVideo(){};
|
virtual ~IVideo(){};
|
||||||
|
|
||||||
virtual void Start() = 0;
|
virtual bool Start() = 0;
|
||||||
virtual void Stop() = 0;
|
virtual void Stop() = 0;
|
||||||
virtual void Pause(bool Pause) = 0;
|
virtual void Pause(bool Pause) = 0;
|
||||||
virtual bool IsRecording() = 0;
|
virtual bool IsRecording() = 0;
|
||||||
|
|
|
@ -92,7 +92,9 @@ public:
|
||||||
virtual void StopVoice(CVoiceHandle Voice) = 0;
|
virtual void StopVoice(CVoiceHandle Voice) = 0;
|
||||||
virtual bool IsPlaying(int SampleId) = 0;
|
virtual bool IsPlaying(int SampleId) = 0;
|
||||||
|
|
||||||
|
virtual int MixingRate() const = 0;
|
||||||
virtual void Mix(short *pFinalOut, unsigned Frames) = 0;
|
virtual void Mix(short *pFinalOut, unsigned Frames) = 0;
|
||||||
|
|
||||||
// useful for thread synchronization
|
// useful for thread synchronization
|
||||||
virtual void PauseAudioDevice() = 0;
|
virtual void PauseAudioDevice() = 0;
|
||||||
virtual void UnpauseAudioDevice() = 0;
|
virtual void UnpauseAudioDevice() = 0;
|
||||||
|
|
|
@ -1962,8 +1962,6 @@ void CMenus::PopupConfirmDemoReplaceVideo()
|
||||||
str_format(aBuf, sizeof(aBuf), "%s/%s.demo", m_aCurrentDemoFolder, m_aCurrentDemoSelectionName);
|
str_format(aBuf, sizeof(aBuf), "%s/%s.demo", m_aCurrentDemoFolder, m_aCurrentDemoSelectionName);
|
||||||
char aVideoName[IO_MAX_PATH_LENGTH];
|
char aVideoName[IO_MAX_PATH_LENGTH];
|
||||||
str_copy(aVideoName, m_DemoRenderInput.GetString());
|
str_copy(aVideoName, m_DemoRenderInput.GetString());
|
||||||
if(!str_endswith(aVideoName, ".mp4"))
|
|
||||||
str_append(aVideoName, ".mp4");
|
|
||||||
const char *pError = Client()->DemoPlayer_Render(aBuf, m_DemolistStorageType, aVideoName, m_Speed, m_StartPaused);
|
const char *pError = Client()->DemoPlayer_Render(aBuf, m_DemolistStorageType, aVideoName, m_Speed, m_StartPaused);
|
||||||
m_Speed = 4;
|
m_Speed = 4;
|
||||||
m_StartPaused = false;
|
m_StartPaused = false;
|
||||||
|
|
|
@ -1084,9 +1084,16 @@ void CMenus::RenderDemoBrowserList(CUIRect ListView, bool &WasListboxItemActivat
|
||||||
|
|
||||||
#if defined(CONF_VIDEORECORDER)
|
#if defined(CONF_VIDEORECORDER)
|
||||||
if(!m_DemoRenderInput.IsEmpty())
|
if(!m_DemoRenderInput.IsEmpty())
|
||||||
|
{
|
||||||
|
if(DemoPlayer()->ErrorMessage()[0] == '\0')
|
||||||
{
|
{
|
||||||
m_Popup = POPUP_RENDER_DONE;
|
m_Popup = POPUP_RENDER_DONE;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_DemoRenderInput.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct SColumn
|
struct SColumn
|
||||||
|
|
Loading…
Reference in a new issue