diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..685054c --- /dev/null +++ b/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 11E53 + CFBundleDevelopmentRegion + {{osx.default_language}} + CFBundleDisplayName + {{project.title}} + CFBundleExecutable + {{exe-name}} + CFBundleIconFile + icon.icns + CFBundleIdentifier + {{osx.bundle_identifier}} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + unnamed + CFBundlePackageType + APPL + CFBundleShortVersionString + {{project.version}} + CFBundleSignature + ???? + CFBundleLocalizations + {{#application-localizations}} + {{.}}{{/application-localizations}} + + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.7 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + NSCameraUsageDescription + App Needs to use camera to take Profile picture + NSPhotoLibraryAddUsageDescription + App Needs to store your photos + {{#display.high_dpi}} + NSHighResolutionCapable + + {{/display.high_dpi}} + + diff --git a/camera/src/camera.cpp b/camera/src/camera.cpp index a4945fd..bc6a948 100644 --- a/camera/src/camera.cpp +++ b/camera/src/camera.cpp @@ -26,26 +26,69 @@ struct DefoldCamera // Information about the currently set camera CameraInfo m_Params; + + dmArray m_MessageQueue; + dmScript::LuaCallbackInfo* m_Callback; + dmMutex::HMutex m_Mutex; }; DefoldCamera g_DefoldCamera; +void Camera_QueueMessage(CameraStatus status) +{ + DM_MUTEX_SCOPED_LOCK(g_DefoldCamera.m_Mutex); + + if (g_DefoldCamera.m_MessageQueue.Full()) + { + g_DefoldCamera.m_MessageQueue.OffsetCapacity(1); + } + g_DefoldCamera.m_MessageQueue.Push(status); +} + +static void Camera_ProcessQueue() +{ + DM_MUTEX_SCOPED_LOCK(g_DefoldCamera.m_Mutex); + + for (uint32_t i = 0; i != g_DefoldCamera.m_MessageQueue.Size(); ++i) + { + lua_State* L = dmScript::GetCallbackLuaContext(g_DefoldCamera.m_Callback); + if (!dmScript::SetupCallback(g_DefoldCamera.m_Callback)) + { + break; + } + CameraStatus status = g_DefoldCamera.m_MessageQueue[i]; + lua_pushnumber(L, (lua_Number)status); + int ret = lua_pcall(L, 2, 0, 0); + if (ret != 0) + { + lua_pop(L, 1); + } + dmScript::TeardownCallback(g_DefoldCamera.m_Callback); + } + g_DefoldCamera.m_MessageQueue.SetSize(0); +} + +static void Camera_DestroyCallback() +{ + if (g_DefoldCamera.m_Callback != 0) + { + dmScript::DestroyCallback(g_DefoldCamera.m_Callback); + g_DefoldCamera.m_Callback = 0; + } +} + static int StartCapture(lua_State* L) { - DM_LUA_STACK_CHECK(L, 1); + DM_LUA_STACK_CHECK(L, 0); CameraType type = (CameraType) luaL_checkint(L, 1); CaptureQuality quality = (CaptureQuality)luaL_checkint(L, 2); - int status = CameraPlatform_StartCapture(&g_DefoldCamera.m_VideoBuffer, type, quality, g_DefoldCamera.m_Params); + Camera_DestroyCallback(); + g_DefoldCamera.m_Callback = dmScript::CreateCallback(L, 3); - lua_pushboolean(L, status > 0); - if( status == 0 ) - { - dmLogError("capture failed!\n"); - return 1; - } + CameraPlatform_StartCapture(&g_DefoldCamera.m_VideoBuffer, type, quality, g_DefoldCamera.m_Params); // Increase ref count dmScript::LuaHBuffer luabuffer = {g_DefoldCamera.m_VideoBuffer, false}; @@ -59,14 +102,9 @@ static int StopCapture(lua_State* L) { DM_LUA_STACK_CHECK(L, 0); - int status = CameraPlatform_StopCapture(); - if( !status ) - { - return luaL_error(L, "Failed to stop capture. Was it started?"); - } + CameraPlatform_StopCapture(); dmScript::Unref(L, LUA_REGISTRYINDEX, g_DefoldCamera.m_VideoBufferLuaRef); // We want it destroyed by the GC - return 0; } @@ -94,7 +132,7 @@ static int GetInfo(lua_State* L) static int GetFrame(lua_State* L) { DM_LUA_STACK_CHECK(L, 1); - lua_rawgeti(L,LUA_REGISTRYINDEX, g_DefoldCamera.m_VideoBufferLuaRef); + lua_rawgeti(L,LUA_REGISTRYINDEX, g_DefoldCamera.m_VideoBufferLuaRef); return 1; } @@ -113,9 +151,9 @@ static void LuaInit(lua_State* L) int top = lua_gettop(L); luaL_register(L, MODULE_NAME, Module_methods); -#define SETCONSTANT(name) \ - lua_pushnumber(L, (lua_Number) name); \ - lua_setfield(L, -2, #name);\ + #define SETCONSTANT(name) \ + lua_pushnumber(L, (lua_Number) name); \ + lua_setfield(L, -2, #name);\ SETCONSTANT(CAMERA_TYPE_FRONT) SETCONSTANT(CAMERA_TYPE_BACK) @@ -124,7 +162,12 @@ static void LuaInit(lua_State* L) SETCONSTANT(CAPTURE_QUALITY_MEDIUM) SETCONSTANT(CAPTURE_QUALITY_HIGH) -#undef SETCONSTANT + SETCONSTANT(STATUS_STARTED) + SETCONSTANT(STATUS_STOPPED) + SETCONSTANT(STATUS_NOT_PERMITTED) + SETCONSTANT(STATUS_ERROR) + + #undef SETCONSTANT lua_pop(L, 1); assert(top == lua_gettop(L)); @@ -132,12 +175,20 @@ static void LuaInit(lua_State* L) dmExtension::Result AppInitializeCamera(dmExtension::AppParams* params) { + dmLogInfo("Registered %s Extension", MODULE_NAME); return dmExtension::RESULT_OK; } dmExtension::Result InitializeCamera(dmExtension::Params* params) { LuaInit(params->m_L); + g_DefoldCamera.m_Mutex = dmMutex::New(); + return dmExtension::RESULT_OK; +} + +static dmExtension::Result UpdateCamera(dmExtension::Params* params) +{ + Camera_ProcessQueue(); return dmExtension::RESULT_OK; } @@ -148,6 +199,8 @@ dmExtension::Result AppFinalizeCamera(dmExtension::AppParams* params) dmExtension::Result FinalizeCamera(dmExtension::Params* params) { + dmMutex::Delete(g_DefoldCamera.m_Mutex); + Camera_DestroyCallback(); return dmExtension::RESULT_OK; } @@ -156,7 +209,7 @@ dmExtension::Result FinalizeCamera(dmExtension::Params* params) static dmExtension::Result AppInitializeCamera(dmExtension::AppParams* params) { - dmLogInfo("Registered %s (null) Extension\n", MODULE_NAME); + dmLogInfo("Registered %s (null) Extension", MODULE_NAME); return dmExtension::RESULT_OK; } @@ -165,6 +218,12 @@ static dmExtension::Result InitializeCamera(dmExtension::Params* params) return dmExtension::RESULT_OK; } +static dmExtension::Result UpdateCamera(dmExtension::Params* params) +{ + Camera_ProcessQueue() + return dmExtension::RESULT_OK; +} + static dmExtension::Result AppFinalizeCamera(dmExtension::AppParams* params) { return dmExtension::RESULT_OK; @@ -178,4 +237,4 @@ static dmExtension::Result FinalizeCamera(dmExtension::Params* params) #endif // platforms -DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, AppInitializeCamera, AppFinalizeCamera, InitializeCamera, 0, 0, FinalizeCamera) +DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, AppInitializeCamera, AppFinalizeCamera, InitializeCamera, UpdateCamera, 0, FinalizeCamera) diff --git a/camera/src/camera.mm b/camera/src/camera.mm index 9c466ae..866c2ed 100644 --- a/camera/src/camera.mm +++ b/camera/src/camera.mm @@ -121,11 +121,10 @@ IOSCamera g_Camera; } } -- (void)captureOutput:(AVCaptureOutput *)captureOutput - didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer +- (void)captureOutput:(AVCaptureOutput *)captureOutput + didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { - //NSLog(@"DROPPING FRAME!!!"); } @@ -256,7 +255,7 @@ IOSCamera g_Camera; static CMVideoDimensions FlipCoords(AVCaptureVideoDataOutput* output, const CMVideoDimensions& in) { - CMVideoDimensions out = in; + CMVideoDimensions out = in; #if defined(DM_PLATFORM_IOS) AVCaptureConnection* conn = [output connectionWithMediaType:AVMediaTypeVideo]; switch (conn.videoOrientation) { @@ -274,7 +273,7 @@ static CMVideoDimensions FlipCoords(AVCaptureVideoDataOutput* output, const CMVi - ( BOOL ) startCamera: (AVCaptureDevicePosition) cameraPosition - quality: (CaptureQuality)quality + quality: (CaptureQuality)quality { // 1. Find the back camera if ( ![ self findCamera: cameraPosition ] ) @@ -352,7 +351,7 @@ static CMVideoDimensions FlipCoords(AVCaptureVideoDataOutput* output, const CMVi @end -int CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams) +void CameraPlatform_StartCaptureAuthorized(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams) { if(g_Camera.m_Delegate == 0) { @@ -381,10 +380,57 @@ int CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, Capt g_Camera.m_VideoBuffer = *buffer; - return started ? 1 : 0; + if (started) + { + Camera_QueueMessage(STATUS_STARTED); + } + else + { + Camera_QueueMessage(STATUS_ERROR); + } } -int CameraPlatform_StopCapture() +void CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams) +{ + // Request permission to access the camera. + int status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; + if (status == AVAuthorizationStatusAuthorized) + { + // The user has previously granted access to the camera. + dmLogInfo("AVAuthorizationStatusAuthorized"); + CameraPlatform_StartCaptureAuthorized(buffer, type, quality, outparams); + } + else if (status == AVAuthorizationStatusNotDetermined) + { + dmLogInfo("AVAuthorizationStatusNotDetermined"); + // The app hasn't yet asked the user for camera access. + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { + if (granted) { + dmLogInfo("AVAuthorizationStatusNotDetermined - granted!"); + CameraPlatform_StartCaptureAuthorized(buffer, type, quality, outparams); + } + else + { + dmLogInfo("AVAuthorizationStatusNotDetermined - not granted!"); + Camera_QueueMessage(STATUS_NOT_PERMITTED); + } + }]; + } + else if (status == AVAuthorizationStatusDenied) + { + // The user has previously denied access. + dmLogInfo("AVAuthorizationStatusDenied"); + Camera_QueueMessage(STATUS_NOT_PERMITTED); + } + else if (status == AVAuthorizationStatusRestricted) + { + // The user can't grant access due to restrictions. + dmLogInfo("AVAuthorizationStatusRestricted"); + Camera_QueueMessage(STATUS_NOT_PERMITTED); + } +} + +void CameraPlatform_StopCapture() { if(g_Camera.m_Delegate != 0) { @@ -395,7 +441,6 @@ int CameraPlatform_StopCapture() dmBuffer::Destroy(g_Camera.m_VideoBuffer); g_Camera.m_VideoBuffer = 0; } - return 1; } #endif // DM_PLATFORM_IOS/DM_PLATFORM_OSX diff --git a/camera/src/camera_private.h b/camera/src/camera_private.h index f0f6131..206a178 100644 --- a/camera/src/camera_private.h +++ b/camera/src/camera_private.h @@ -22,5 +22,15 @@ struct CameraInfo CameraType m_Type; }; -extern int CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams); -extern int CameraPlatform_StopCapture(); +enum CameraStatus +{ + STATUS_STARTED, + STATUS_STOPPED, + STATUS_NOT_PERMITTED, + STATUS_ERROR +}; + +extern void CameraPlatform_StartCapture(dmBuffer::HBuffer* buffer, CameraType type, CaptureQuality quality, CameraInfo& outparams); +extern void CameraPlatform_StopCapture(); + +void Camera_QueueMessage(CameraStatus message); diff --git a/game.project b/game.project index 421ce8a..db43bea 100644 --- a/game.project +++ b/game.project @@ -26,6 +26,7 @@ package = com.defold.camera [osx] bundle_identifier = com.defold.camera +infoplist = /Info.plist [library] include_dirs = camera diff --git a/main/main.script b/main/main.script index 6bbd636..b1bd207 100644 --- a/main/main.script +++ b/main/main.script @@ -6,44 +6,40 @@ local function stop_capture(self) self.cameraframe = nil camera.stop_capture() - + label.set_text("logo#status", "Capture Status: OFF") end local function start_capture(self) - if self.cameraframe ~= nil then + if not camera then + label.set_text("logo#status", "Capture Status: UNAVAILABLE") return end - - if camera ~= nil then - local sysinfo = sys.get_sys_info() - - local quality = camera.CAPTURE_QUALITY_HIGH - local type = camera.CAMERA_TYPE_FRONT - if sysinfo.system_name == 'iPhone OS' then - type = camera.CAMERA_TYPE_BACK - quality = camera.CAPTURE_QUALITY_MEDIUM - end - - if camera.start_capture(type, quality) then + local sysinfo = sys.get_sys_info() + + local quality = camera.CAPTURE_QUALITY_HIGH + local type = camera.CAMERA_TYPE_FRONT + if sysinfo.system_name == 'iPhone OS' then + type = camera.CAMERA_TYPE_BACK + quality = camera.CAPTURE_QUALITY_MEDIUM + end + + camera.start_capture(type, quality, function(self, status) + if status == camera.STATUS_STARTED then self.cameraframe = camera.get_frame() self.camerainfo = camera.get_info() - print("Initialized camera") - pprint(self.camerainfo) - self.cameratextureheader = {width=self.camerainfo.width, - height=self.camerainfo.height, - type=resource.TEXTURE_TYPE_2D, - format=resource.TEXTURE_FORMAT_RGB, - num_mip_maps=1 } + height=self.camerainfo.height, + type=resource.TEXTURE_TYPE_2D, + format=resource.TEXTURE_FORMAT_RGB, + num_mip_maps=1 } + else + label.set_text("logo#status", "Capture Status: ERROR") end - label.set_text("logo#status", "Capture Status: ON") - else - print("could not start camera capture") - label.set_text("logo#status", "Capture Status: UNAVAILABLE") - end + end) + label.set_text("logo#status", "Capture Status: ON") end function init(self) @@ -54,7 +50,7 @@ function init(self) local screen_height = sys.get_config("display.height", 800) local scale_width = screen_width / logosize local scale_height = screen_height / logosize - + go.set("#sprite", "scale", vmath.vector3(scale_width, scale_height, 1) ) @@ -73,16 +69,15 @@ function update(self, dt) local pathmodelcamera = go.get("#sprite", "texture0") resource.set_texture(pathmodelcamera, self.cameratextureheader, self.cameraframe) end - end function on_input(self, action_id, action) - if (action_id == hash("space") or action_id == hash("touch")) and action.pressed then + --[[if (action_id == hash("space") or action_id == hash("touch")) and action.pressed then if self.cameraframe == nil then start_capture(self) else stop_capture(self) end - end + end--]] end