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();
|
||||
// pause the sound device while creating the video instance
|
||||
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();
|
||||
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)
|
||||
{
|
||||
IVideo::Current()->Pause(true);
|
||||
|
|
|
@ -131,7 +131,9 @@ public:
|
|||
void StopVoice(CVoiceHandle Voice) 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 PauseAudioDevice() override;
|
||||
void UnpauseAudioDevice() override;
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,39 +15,38 @@ extern "C" {
|
|||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#define ALEN 2048
|
||||
|
||||
class CGraphics_Threaded;
|
||||
class IGraphics;
|
||||
class ISound;
|
||||
class IStorage;
|
||||
|
||||
extern CLock g_WriteLock;
|
||||
|
||||
// a wrapper around a single output AVStream
|
||||
struct OutputStream
|
||||
class COutputStream
|
||||
{
|
||||
AVStream *pSt = nullptr;
|
||||
AVCodecContext *pEnc = nullptr;
|
||||
public:
|
||||
AVStream *m_pStream = nullptr;
|
||||
AVCodecContext *m_pCodecContext = nullptr;
|
||||
|
||||
/* pts of the next frame that will be generated */
|
||||
int64_t NextPts = 0;
|
||||
int64_t m_SamplesCount = 0;
|
||||
int64_t m_SamplesFrameCount = 0;
|
||||
|
||||
std::vector<AVFrame *> m_vpFrames;
|
||||
std::vector<AVFrame *> m_vpTmpFrames;
|
||||
|
||||
std::vector<struct SwsContext *> m_vpSwsCtxs;
|
||||
std::vector<struct SwrContext *> m_vpSwrCtxs;
|
||||
std::vector<struct SwsContext *> m_vpSwsContexts;
|
||||
std::vector<struct SwrContext *> m_vpSwrContexts;
|
||||
};
|
||||
|
||||
class CVideo : public IVideo
|
||||
{
|
||||
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();
|
||||
|
||||
void Start() override REQUIRES(!g_WriteLock);
|
||||
bool Start() override REQUIRES(!g_WriteLock);
|
||||
void Stop() override;
|
||||
void Pause(bool Pause) override;
|
||||
bool IsRecording() override { return m_Recording; }
|
||||
|
@ -60,12 +59,12 @@ public:
|
|||
|
||||
static IVideo *Current() { return IVideo::ms_pCurrentVideo; }
|
||||
|
||||
static void Init() { av_log_set_level(AV_LOG_DEBUG); }
|
||||
static void Init();
|
||||
|
||||
private:
|
||||
void RunVideoThread(size_t ParentThreadIndex, 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 FillAudioFrame(size_t ThreadIndex);
|
||||
|
@ -75,27 +74,26 @@ private:
|
|||
AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height);
|
||||
AVFrame *AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples);
|
||||
|
||||
void WriteFrame(OutputStream *pStream, size_t ThreadIndex) REQUIRES(g_WriteLock);
|
||||
void FinishFrames(OutputStream *pStream);
|
||||
void CloseStream(OutputStream *pStream);
|
||||
void WriteFrame(COutputStream *pStream, size_t ThreadIndex) REQUIRES(g_WriteLock);
|
||||
void FinishFrames(COutputStream *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;
|
||||
ISound *m_pSound;
|
||||
|
||||
int m_Width;
|
||||
int m_Height;
|
||||
char m_aName[256];
|
||||
//FILE *m_dbgfile;
|
||||
uint64_t m_VSeq = 0;
|
||||
uint64_t m_ASeq = 0;
|
||||
uint64_t m_Vframe;
|
||||
uint64_t m_VideoFrameIndex = 0;
|
||||
uint64_t m_AudioFrameIndex = 0;
|
||||
|
||||
int m_FPS;
|
||||
|
||||
bool m_Started;
|
||||
bool m_Stopped;
|
||||
bool m_Recording;
|
||||
|
||||
size_t m_VideoThreads = 2;
|
||||
|
@ -103,8 +101,9 @@ private:
|
|||
size_t m_AudioThreads = 2;
|
||||
size_t m_CurAudioThreadIndex = 0;
|
||||
|
||||
struct SVideoRecorderThread
|
||||
class CVideoRecorderThread
|
||||
{
|
||||
public:
|
||||
std::thread m_Thread;
|
||||
std::mutex m_Mutex;
|
||||
std::condition_variable m_Cond;
|
||||
|
@ -118,10 +117,11 @@ private:
|
|||
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::mutex m_Mutex;
|
||||
std::condition_variable m_Cond;
|
||||
|
@ -136,22 +136,28 @@ private:
|
|||
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_ProcessingAudioFrame;
|
||||
|
||||
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<std::vector<uint8_t>> m_vPixelHelper;
|
||||
std::vector<CVideoBuffer> m_vVideoBuffers;
|
||||
class CAudioBuffer
|
||||
{
|
||||
public:
|
||||
int16_t m_aBuffer[4096];
|
||||
};
|
||||
std::vector<CAudioBuffer> m_vAudioBuffers;
|
||||
|
||||
OutputStream m_VideoStream;
|
||||
OutputStream m_AudioStream;
|
||||
COutputStream m_VideoStream;
|
||||
COutputStream m_AudioStream;
|
||||
|
||||
const AVCodec *m_pVideoCodec;
|
||||
const AVCodec *m_pAudioCodec;
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
virtual int SetPos(int WantedTick) = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual void Unpause() = 0;
|
||||
virtual const char *ErrorMessage() const = 0;
|
||||
virtual bool IsPlaying() const = 0;
|
||||
virtual const CInfo *BaseInfo() 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; }
|
||||
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;
|
||||
const char *Filename() { return m_aFilename; }
|
||||
const char *ErrorMessage() { return m_aErrorMessage; }
|
||||
const char *Filename() const { return m_aFilename; }
|
||||
const char *ErrorMessage() const override { return m_aErrorMessage; }
|
||||
|
||||
int Update(bool RealTime = true);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ class IVideo
|
|||
public:
|
||||
virtual ~IVideo(){};
|
||||
|
||||
virtual void Start() = 0;
|
||||
virtual bool Start() = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void Pause(bool Pause) = 0;
|
||||
virtual bool IsRecording() = 0;
|
||||
|
|
|
@ -92,7 +92,9 @@ public:
|
|||
virtual void StopVoice(CVoiceHandle Voice) = 0;
|
||||
virtual bool IsPlaying(int SampleId) = 0;
|
||||
|
||||
virtual int MixingRate() const = 0;
|
||||
virtual void Mix(short *pFinalOut, unsigned Frames) = 0;
|
||||
|
||||
// useful for thread synchronization
|
||||
virtual void PauseAudioDevice() = 0;
|
||||
virtual void UnpauseAudioDevice() = 0;
|
||||
|
|
|
@ -1962,8 +1962,6 @@ void CMenus::PopupConfirmDemoReplaceVideo()
|
|||
str_format(aBuf, sizeof(aBuf), "%s/%s.demo", m_aCurrentDemoFolder, m_aCurrentDemoSelectionName);
|
||||
char aVideoName[IO_MAX_PATH_LENGTH];
|
||||
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);
|
||||
m_Speed = 4;
|
||||
m_StartPaused = false;
|
||||
|
|
|
@ -1084,9 +1084,16 @@ void CMenus::RenderDemoBrowserList(CUIRect ListView, bool &WasListboxItemActivat
|
|||
|
||||
#if defined(CONF_VIDEORECORDER)
|
||||
if(!m_DemoRenderInput.IsEmpty())
|
||||
{
|
||||
if(DemoPlayer()->ErrorMessage()[0] == '\0')
|
||||
{
|
||||
m_Popup = POPUP_RENDER_DONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DemoRenderInput.Clear();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct SColumn
|
||||
|
|
Loading…
Reference in a new issue