Cocoa Port: Do a bunch of tweaks and fixes for an upcoming new feature. (Related to commit 932cdd6.)

This commit is contained in:
rogerman 2018-07-27 13:18:48 -07:00
parent bee3fd30ce
commit 232e3d6f51
10 changed files with 1114 additions and 175 deletions

View File

@ -245,7 +245,11 @@ ClientAVCaptureObject::ClientAVCaptureObject(size_t videoFrameWidth, size_t vide
{
__InstanceInit(videoFrameWidth, videoFrameHeight);
_pendingVideoBuffer = (u8 *)malloc_alignedCacheLine(_videoFrameSize * _pendingBufferCount);
if (_videoFrameSize > 0)
{
_pendingVideoBuffer = (u8 *)malloc_alignedCacheLine(_videoFrameSize * _pendingBufferCount);
}
_pendingAudioBuffer = (u8 *)malloc_alignedCacheLine(_audioFrameSize * _pendingBufferCount);
_pendingAudioWriteSize = (size_t *)calloc(_pendingBufferCount, sizeof(size_t));
@ -258,7 +262,7 @@ ClientAVCaptureObject::ClientAVCaptureObject(size_t videoFrameWidth, size_t vide
_convertParam[i].firstLineIndex = 0;
_convertParam[i].lastLineIndex = linesPerThread - 1;
_convertParam[i].srcOffset = 0;
_convertParam[i].dstOffset = (videoFrameWidth * (videoFrameHeight - 1) * 3);
_convertParam[i].dstOffset = 0;
_convertParam[i].frameWidth = videoFrameWidth;
}
}
@ -283,7 +287,7 @@ ClientAVCaptureObject::ClientAVCaptureObject(size_t videoFrameWidth, size_t vide
}
_convertParam[i].srcOffset = (videoFrameWidth * _convertParam[i].firstLineIndex);
_convertParam[i].dstOffset = (videoFrameWidth * (videoFrameHeight - (_convertParam[i].firstLineIndex + 1)) * 3);
_convertParam[i].dstOffset = (videoFrameWidth * _convertParam[i].firstLineIndex * 3);
_convertParam[i].frameWidth = videoFrameWidth;
}
}
@ -297,9 +301,20 @@ void ClientAVCaptureObject::__InstanceInit(size_t videoFrameWidth, size_t videoF
_mutexCaptureFlags = slock_new();
_videoFrameWidth = videoFrameWidth;
_videoFrameHeight = videoFrameHeight;
_videoFrameSize = videoFrameWidth * videoFrameHeight * 3; // The video frame will always be in the RGB888 colorspace.
if ( (videoFrameWidth == 0) || (videoFrameHeight == 0) )
{
// Audio-only capture.
_videoFrameWidth = 0;
_videoFrameHeight = 0;
_videoFrameSize = 0;
}
else
{
_videoFrameWidth = videoFrameWidth;
_videoFrameHeight = videoFrameHeight;
_videoFrameSize = videoFrameWidth * videoFrameHeight * 3; // The video frame will always be in the RGB888 colorspace.
}
_audioBlockSize = sizeof(int16_t) * 2;
_audioFrameSize = ((DESMUME_SAMPLE_RATE * _audioBlockSize) / 30);
@ -433,27 +448,24 @@ void ClientAVCaptureObject::StreamWriteStart()
return;
}
// Force video conversion to finish before putting the frame on the queue.
if ( (this->_videoFrameSize > 0) && (this->_numThreads > 0) )
{
for (size_t i = 0; i < this->_numThreads; i++)
{
this->_convertThread[i]->finish();
}
}
const size_t bufferIndex = this->_currentBufferIndex;
const size_t queueSize = this->_fs->GetQueueSize();
const bool isQueueEmpty = (queueSize == 0);
// If there are no frames in the current write queue, then we know that the current
// pending video frame will be written immediately. If this is the case, then we
// need to force the video conversion to finish so that we can write out the frame.
if (isQueueEmpty)
{
if (this->_videoFrameSize > 0)
{
for (size_t i = 0; i < this->_numThreads; i++)
{
this->_convertThread[i]->finish();
}
}
}
this->_fs->QueueAdd(this->_pendingVideoBuffer + (this->_videoFrameSize * bufferIndex), this->_videoFrameSize,
this->_pendingAudioBuffer + (AUDIO_STREAM_BUFFER_SIZE * bufferIndex), this->_pendingAudioWriteSize[bufferIndex]);
// If the queue was initially empty, then we need to wake up the file write
// thread at this time.
if (isQueueEmpty)
{
this->_fileWriteThread->execute(&RunAviFileWrite, this->_fs);
@ -468,39 +480,31 @@ void ClientAVCaptureObject::StreamWriteFinish()
//converts 16bpp to 24bpp and flips
void ClientAVCaptureObject::ConvertVideoSlice555Xto888(const VideoConvertParam &param)
{
const size_t lineCount = param.lastLineIndex - param.firstLineIndex + 1;
const u16 *__restrict src = (const u16 *__restrict)param.src;
u8 *__restrict dst = param.dst;
for (size_t y = param.firstLineIndex; y <= param.lastLineIndex; y++)
{
ColorspaceConvertBuffer555XTo888<true, false>(src, dst, param.frameWidth);
src += param.frameWidth;
dst -= param.frameWidth * 3;
}
ColorspaceConvertBuffer555XTo888<false, false>(src, dst, param.frameWidth * lineCount);
}
//converts 32bpp to 24bpp and flips
void ClientAVCaptureObject::ConvertVideoSlice888Xto888(const VideoConvertParam &param)
{
const size_t lineCount = param.lastLineIndex - param.firstLineIndex + 1;
const u32 *__restrict src = (const u32 *__restrict)param.src;
u8 *__restrict dst = param.dst;
for (size_t y = param.firstLineIndex; y <= param.lastLineIndex; y++)
{
ColorspaceConvertBuffer888XTo888<true, false>(src, dst, param.frameWidth);
src += param.frameWidth;
dst -= param.frameWidth * 3;
}
ColorspaceConvertBuffer888XTo888<false, false>(src, dst, param.frameWidth * lineCount);
}
void ClientAVCaptureObject::ReadVideoFrame(const void *srcVideoFrame, const size_t inFrameWidth, const size_t inFrameHeight, const NDSColorFormat colorFormat)
void ClientAVCaptureObject::CaptureVideoFrame(const void *srcVideoFrame, const size_t inFrameWidth, const size_t inFrameHeight, const NDSColorFormat colorFormat)
{
//dont do anything if prescale has changed, it's just going to be garbage
if (!this->_isCapturingVideo ||
(srcVideoFrame == NULL) ||
(this->_videoFrameSize == 0) ||
(this->_videoFrameWidth != inFrameWidth) ||
(this->_videoFrameHeight != (inFrameHeight * 2)))
(this->_videoFrameHeight != inFrameHeight))
{
return;
}
@ -548,7 +552,7 @@ void ClientAVCaptureObject::ReadVideoFrame(const void *srcVideoFrame, const size
}
}
void ClientAVCaptureObject::ReadAudioFrames(const void *srcAudioBuffer, const size_t inSampleCount)
void ClientAVCaptureObject::CaptureAudioFrames(const void *srcAudioBuffer, const size_t inSampleCount)
{
if (!this->_isCapturingAudio || (srcAudioBuffer == NULL))
{

View File

@ -253,8 +253,8 @@ public:
void ConvertVideoSlice555Xto888(const VideoConvertParam &param);
void ConvertVideoSlice888Xto888(const VideoConvertParam &param);
void ReadVideoFrame(const void *srcVideoFrame, const size_t inFrameWidth, const size_t inFrameHeight, const NDSColorFormat colorFormat);
void ReadAudioFrames(const void *srcAudioBuffer, const size_t inSampleCount);
void CaptureVideoFrame(const void *srcVideoFrame, const size_t inFrameWidth, const size_t inFrameHeight, const NDSColorFormat colorFormat);
void CaptureAudioFrames(const void *srcAudioBuffer, const size_t inSampleCount);
};
#endif // _CLIENT_AV_CAPTURE_OBJECT_H_

View File

@ -25,6 +25,7 @@
#include "../../gdbstub.h"
#include "../../rtc.h"
#include "ClientAVCaptureObject.h"
#include "ClientExecutionControl.h"
// Need to include assert.h this way so that GDB stub will work
@ -144,9 +145,15 @@ ClientAVCaptureObject* ClientExecutionControl::GetClientAVCaptureObjectApplied()
void ClientExecutionControl::SetClientAVCaptureObject(ClientAVCaptureObject *theCaptureObject)
{
pthread_mutex_lock(&this->_mutexSettingsPendingOnNDSExec);
this->_settingsPending.avCaptureObject = theCaptureObject;
this->_newSettingsPendingOnNDSExec = true;
if (this->_settingsPending.avCaptureObject != theCaptureObject)
{
this->_settingsPending.avCaptureObject = theCaptureObject;
this->_needResetFramesToSkip = true;
this->_newSettingsPendingOnNDSExec = true;
}
pthread_mutex_unlock(&this->_mutexSettingsPendingOnNDSExec);
}

View File

@ -22,11 +22,13 @@
#include <map>
#include <string>
#include "ClientAVCaptureObject.h"
#include "ClientInputHandler.h"
#include "../../slot1.h"
#ifdef BOOL
#undef BOOL
#endif
#define SPEED_SCALAR_QUARTER 0.25 // Speed scalar for quarter execution speed.
#define SPEED_SCALAR_HALF 0.5 // Speed scalar for half execution speed.
@ -43,6 +45,8 @@
#define FRAME_SKIP_BIAS 0.1 // May be any real number. This value acts as a vector addition to the frame skip.
#define MAX_FRAME_SKIP (DS_FRAMES_PER_SECOND / 2.98)
class ClientAVCaptureObject;
enum ExecutionBehavior
{
ExecutionBehavior_Pause = 0,

View File

@ -26,6 +26,7 @@
#import "cocoa_util.h"
#include "macOS_driver.h"
#include "ClientAVCaptureObject.h"
#include "ClientExecutionControl.h"
#include "ClientInputHandler.h"
@ -1118,7 +1119,7 @@ static void* RunCoreThread(void *arg)
continue;
}
if (avCaptureObject != NULL)
if ( (avCaptureObject != NULL) && !avCaptureObject->IsCapturingVideo() )
{
avCaptureObject->StreamWriteStart();
}
@ -1186,7 +1187,7 @@ static void* RunCoreThread(void *arg)
{
case ExecutionBehavior_Run:
{
if (execControl->GetEnableFrameSkipApplied())
if (execControl->GetEnableFrameSkipApplied() && !avCaptureObject)
{
if (framesToSkip > 0)
{
@ -1203,14 +1204,17 @@ static void* RunCoreThread(void *arg)
case ExecutionBehavior_FrameJump:
{
if (framesToSkip > 0)
if (!avCaptureObject)
{
NDS_SkipNextFrame();
execControl->SetFramesToSkip(framesToSkip - 1);
}
else
{
execControl->SetFramesToSkip( (uint8_t)((DS_FRAMES_PER_SECOND * 1.0) + 0.85) );
if (framesToSkip > 0)
{
NDS_SkipNextFrame();
execControl->SetFramesToSkip(framesToSkip - 1);
}
else
{
execControl->SetFramesToSkip( (uint8_t)((DS_FRAMES_PER_SECOND * 1.0) + 0.85) );
}
}
break;
}

View File

@ -23,9 +23,13 @@
#import "cocoa_util.h"
#include "ClientDisplayView.h"
#include "ClientExecutionControl.h"
#include "ClientAVCaptureObject.h"
#undef BOOL
#ifdef BOOL
#undef BOOL
#endif
class ClientAVCaptureObject;
@class NSImage;
@class NSBitmapImageRep;
@ -122,9 +126,14 @@
@interface CocoaDSVideoCapture : CocoaDSDisplay
{
ClientDisplay3DPresenter *_cdp;
ClientAVCaptureObject *avCaptureObject;
uint32_t *_videoCaptureBuffer;
pthread_mutex_t _mutexCaptureBuffer;
}
@property (assign, nonatomic, getter=clientDisplay3DPresenter, setter=setClientDisplay3DPresenter:) ClientDisplay3DPresenter *_cdp;
@property (assign, nonatomic) ClientAVCaptureObject *avCaptureObject;
- (void) handleReceiveGPUFrame;

View File

@ -23,6 +23,8 @@
#import "cocoa_core.h"
#include "sndOSX.h"
#include "ClientAVCaptureObject.h"
#include "../../NDSSystem.h"
#include "../../common.h"
#include "../../GPU.h"
@ -34,7 +36,9 @@
#import <Cocoa/Cocoa.h>
#ifdef BOOL
#undef BOOL
#endif
@implementation CocoaDSOutput
@ -556,21 +560,63 @@
@implementation CocoaDSVideoCapture
@synthesize _cdp;
@synthesize avCaptureObject;
- (id)init
{
self = [super init];
if (self == nil)
{
return self;
}
_cdp = NULL;
avCaptureObject = NULL;
_videoCaptureBuffer = NULL;
pthread_mutex_init(&_mutexCaptureBuffer, NULL);
return self;
}
- (void)dealloc
{
pthread_mutex_lock(&_mutexCaptureBuffer);
free_aligned(_videoCaptureBuffer);
pthread_mutex_unlock(&_mutexCaptureBuffer);
pthread_mutex_destroy(&_mutexCaptureBuffer);
[super dealloc];
}
- (void) handleReceiveGPUFrame
{
[super handleReceiveGPUFrame];
if (_cdp == NULL)
if ( (_cdp == NULL) || (avCaptureObject == NULL) )
{
return;
}
const ClientDisplayPresenterProperties &cdpProperty = _cdp->GetPresenterProperties();
_cdp->LoadDisplays();
_cdp->ProcessDisplays();
_cdp->UpdateLayout();
//_cdp->CopyFrameToBuffer((uint32_t *)[newImageRep bitmapData]);
//avCaptureObject->ReadVideoFrame(NULL, 0, 0, NDSColorFormat_BGR888_Rev);
pthread_mutex_lock(&_mutexCaptureBuffer);
if (_videoCaptureBuffer == NULL)
{
_videoCaptureBuffer = (uint32_t *)malloc_alignedPage(cdpProperty.clientWidth * cdpProperty.clientHeight * sizeof(uint32_t));
}
_cdp->CopyFrameToBuffer(_videoCaptureBuffer);
avCaptureObject->CaptureVideoFrame(_videoCaptureBuffer, (size_t)cdpProperty.clientWidth, (size_t)cdpProperty.clientHeight, NDSColorFormat_BGR888_Rev);
avCaptureObject->StreamWriteStart();
pthread_mutex_unlock(&_mutexCaptureBuffer);
}
@end

View File

@ -16,8 +16,10 @@
*/
#include "macOS_driver.h"
#include "ClientAVCaptureObject.h"
#include "ClientExecutionControl.h"
pthread_mutex_t* macOS_driver::GetCoreThreadMutexLock()
{
return this->__mutexThreadExecute;
@ -52,7 +54,7 @@ void macOS_driver::AVI_SoundUpdate(void *soundData, int soundLen)
return;
}
avCaptureObject->ReadAudioFrames(soundData, soundLen);
avCaptureObject->CaptureAudioFrames(soundData, soundLen);
}
bool macOS_driver::AVI_IsRecording()

View File

@ -2739,6 +2739,11 @@
{
[(NSMenuItem*)theItem setTitle:([cdsCore isFrameSkipEnabled]) ? NSSTRING_TITLE_DISABLE_AUTO_FRAME_SKIP : NSSTRING_TITLE_ENABLE_AUTO_FRAME_SKIP];
}
if ([avCaptureToolDelegate isRecording])
{
enable = NO;
}
}
else if (theAction == @selector(toggleCheats:))
{