/*---------------------------------------------------------------------------* Project: Horizon File: camera.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 46365 $ *---------------------------------------------------------------------------*/ #include #include #include #include "camera.h" #include // The buffer to receive the camera capture data must be 4-byte aligned. // However, when it is aligned to less than 64-bytes, the transfer speed may drop. u8 s_YuvBuffer[2][CAMERA_IMG_WIDTH * CAMERA_IMG_HEIGHT * Y2R_YUV_BYTES] NN_ATTRIBUTE_ALIGN(64); // Buffer to save camera data // Allocated from device memory u8* s_RgbBuffer; void TransferThreadFunc(CameraDemo* pCameraDemo) { pCameraDemo->TransferThreadFuncImpl(); } void CameraDemo::TransferThreadFuncImpl(void) { nn::os::Event *events[EVENT_CAMERA_NUM] = {&m_TransferEvent, &m_ErrorEvent, &m_EndEvent}; // Configure the transfer nn::camera::CTR::SetReceiving(events[EVENT_CAMERA_TRANSFER], reinterpret_cast(s_YuvBuffer[0]), nn::camera::PORT_CAM1, m_CameraOutputSize, m_TransferUnit); u32 yuvWp = 0; while(1) { s32 num = nn::os::WaitObject::WaitAny(reinterpret_cast(events), EVENT_CAMERA_NUM); if(num == EVENT_CAMERA_ERROR) { // Error occurred NN_LOG("camera: An error or reboot occurred\n"); // Restart capture nn::camera::CTR::SetReceiving(events[EVENT_CAMERA_TRANSFER], reinterpret_cast(s_YuvBuffer[yuvWp]), nn::camera::PORT_CAM1, m_CameraOutputSize, m_TransferUnit); nn::camera::CTR::StartCapture(nn::camera::PORT_CAM1); } else if(num == EVENT_CAMERA_TRANSFER) { // Transfer completed yuvWp ^= 1; nn::camera::CTR::SetReceiving(events[EVENT_CAMERA_TRANSFER], reinterpret_cast(s_YuvBuffer[yuvWp]), nn::camera::PORT_CAM1, m_CameraOutputSize, m_TransferUnit); // This demo does not support stopping Y2R before sleep, but since it only displays the previous results regardless of the success or failure of the conversion, there is no problem that would cause stopping nn::y2r::CTR::StopConversion(); while(nn::y2r::CTR::IsBusyConversion()) // Wait because configuration cannot be done during Enable { } // Convert received YUV data with YUVtoRGB. u32 preYuvWp = yuvWp ^ 1; nn::y2r::CTR::SetSendingYuv(reinterpret_cast(s_YuvBuffer[preYuvWp]), m_CameraOutputSize, CAMERA_IMG_WIDTH*Y2R_YUV_BYTES); nn::y2r::CTR::SetReceiving(reinterpret_cast(s_RgbBuffer), m_Y2rOutputSize, CAMERA_IMG_WIDTH*8*Y2R_RGB_BYTES); // An error is returned during sleep, but since this demo only outputs a converted image, there is no need to handle it nn::y2r::CTR::StartConversion(); } else if(num == EVENT_CAMERA_END) { // Exit camera break; } } // Transfer ends } bool CameraDemo::CheckCameraResult( nn::Result result ) { if(result == nn::camera::CTR::ResultIsSleeping()) { // If the system is closed, retry is required return false; } if(result == nn::camera::CTR::ResultFatalError()) { // Each application handles fatal errors as necessary NN_LOG("The camera is broken.\n"); } return true; } void CameraDemo::CameraInitialize() { // Initialize CAMERA nn::Result result = nn::camera::Initialize(); if(!CheckCameraResult(result)) { return; } if ( result == nn::camera::CTR::ResultUsingOtherProcess() ) { // This error usually should not occur // Bug where an applet in the background transitions to the application without exiting the camera NN_UTIL_PANIC_IF_FAILED(result); } // Puts into a state where the camera library initialization completes m_InitializeState = CAMERA_STATE_INITIALIZED; } void CameraDemo::CameraSetting() { // Camera configuration // Specify the number of lines to be accumulated in the buffer s16 transferLine = nn::camera::GetMaxLines( CAMERA_IMG_WIDTH, CAMERA_IMG_HEIGHT ); if(CAMERA_IMG_WIDTH < 16) // Because the GetMaxLines function gets the maximum number of lines, the original number of lines may be less than that. { transferLine = CAMERA_IMG_HEIGHT; } nn::camera::SetTransferLines( nn::camera::PORT_CAM1, transferLine, CAMERA_IMG_WIDTH, CAMERA_IMG_HEIGHT ); // Change the frame rate nn::Result result = nn::camera::SetFrameRate(CAMERA_SELECT, CAMERA_FRAME_RATE); if(!CheckCameraResult(result)) { return; } // Change image size result = nn::camera::SetSize(CAMERA_SELECT, CAMERA_SIZE, nn::camera::CONTEXT_A); if(!CheckCameraResult(result)) { return; } // Put camera in startup state result = nn::camera::Activate(CAMERA_SELECT); if(!CheckCameraResult(result)) { return; } // Set trimming nn::camera::SetTrimming(nn::camera::PORT_CAM1, true); nn::camera::SetTrimmingParamsCenter(nn::camera::PORT_CAM1, CAMERA_IMG_WIDTH, CAMERA_IMG_HEIGHT, CAMERA_WIDTH, CAMERA_HEIGHT); // Set the conversion coefficients nn::y2r::CTR::StandardCoefficient no; result = nn::camera::CTR::GetSuitableY2rStandardCoefficient(&no); if(!CheckCameraResult(result)) { return; } nn::y2r::CTR::SetStandardCoefficient( no ); // Calculate number of loops from the size of the image to transfer m_TransferUnit = nn::camera::GetTransferBytes(nn::camera::PORT_CAM1); m_CameraOutputSize = nn::camera::GetFrameBytes(CAMERA_IMG_WIDTH, CAMERA_IMG_HEIGHT); m_Y2rOutputSize = nn::y2r::GetOutputImageSize(CAMERA_IMG_WIDTH, CAMERA_IMG_HEIGHT, Y2R_OUTPUT_FORMAT); // Get camera interrupt handle nn::camera::GetBufferErrorInterruptEvent(&m_ErrorEvent, nn::camera::PORT_CAM1); m_InitializeState = CAMERA_STATE_SET; // Create thread for camera error/data transfer processing m_TransferThread.StartUsingAutoStack( TransferThreadFunc, this, 4096, nn::os::DEFAULT_THREAD_PRIORITY - 1); // It has a higher priority than the main thread } void CameraDemo::Initialize(demo::RenderSystemDrawing* p_RenderSystem, nn::fnd::ExpHeap* p_AppHeap) { Device::Initialize(p_RenderSystem); mp_AppHeap = p_AppHeap; s_RgbBuffer = reinterpret_cast(mp_AppHeap->Allocate(CAMERA_IMG_WIDTH * CAMERA_IMG_HEIGHT * Y2R_RGB_BYTES, 64)); // Initialize CAMERA CameraInitialize(); // Initialize Y2R nn::y2r::Initialize(); // Set Y2R // Forcibly stop any conversion that is in progress nn::y2r::StopConversion(); while(nn::y2r::IsBusyConversion()) // Wait because configuration cannot be done during Enable { } // Change input format nn::y2r::SetInputFormat(Y2R_INPUT_FORMAT); // Change output format nn::y2r::SetOutputFormat(Y2R_OUTPUT_FORMAT); // Change angle of rotation for output data nn::y2r::SetRotation(Y2R_ROTATION); // Change order of output data nn::y2r::SetBlockAlignment(Y2R_BLOCK_ALIGNMENT); // Specify input data size nn::y2r::SetInputLineWidth(CAMERA_IMG_WIDTH); nn::y2r::SetInputLines(CAMERA_IMG_HEIGHT); // Set alpha value (not used for this demo) nn::y2r::SetAlpha(0xFF); // Set CAMERA if already initialized if ( m_InitializeState == CAMERA_STATE_INITIALIZED ) { CameraSetting(); } } void CameraDemo::Finalize() { // End transfer processing thread m_EndEvent.Signal(); m_TransferThread.Join(); m_TransferThread.Finalize(); nn::y2r::Finalize(); nn::camera::Finalize(); // Free RGB buffer mp_AppHeap->Free(s_RgbBuffer); Device::Finalize(); } void CameraDemo::CameraInitialSequence() { if ( m_InitializeState == CAMERA_STATE_NONE ) { CameraInitialize(); } if ( m_InitializeState == CAMERA_STATE_INITIALIZED ) { CameraSetting(); } if ( m_InitializeState == CAMERA_STATE_SET ) { // Start camera capture nn::camera::ClearBuffer(nn::camera::PORT_CAM1); nn::camera::StartCapture(nn::camera::PORT_CAM1); m_InitializeState = CAMERA_STATE_STARTED; } } void CameraDemo::Start() { Device::Start(); // Try again if Initialize failed because the system was closed CameraInitialSequence(); } void CameraDemo::End() { Device::End(); } void CameraDemo::DrawFrame(void) { // Try again if Initialize failed because the system was closed CameraInitialSequence(); static GLuint textureId = 0; if( textureId != 0 ) { mp_RenderSystem->DeleteTexture(textureId); } GLenum target = GL_TEXTURE_2D | NN_GX_MEM_FCRAM | GL_NO_COPY_FCRAM_DMP; GLenum internalFormat = GL_IMG_FORMAT; GLenum format = GL_IMG_FORMAT; GLenum type = GL_IMG_TYPE; u32 textureWidth = CAMERA_IMG_WIDTH; u32 textureHeight = CAMERA_IMG_HEIGHT; mp_RenderSystem->GenerateTexture(target, internalFormat, textureWidth, textureHeight, format, type, s_RgbBuffer, textureId); mp_RenderSystem->SetColor(0.0f, 0.0f, 1.0f, 0.0f); f32 windowPositionX = 0.0f; f32 windowPositionY = 0.0f; f32 rectangleWidth = CAMERA_IMG_WIDTH; f32 rectangleHeight = CAMERA_IMG_HEIGHT; mp_RenderSystem->FillTexturedRectangle(textureId, windowPositionX, windowPositionY, rectangleWidth, rectangleHeight, CAMERA_IMG_WIDTH, CAMERA_IMG_HEIGHT, textureWidth, textureHeight); } /*---------------------------------------------------------------------------* End of file *---------------------------------------------------------------------------*/