1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     FsSampleStreaming.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 46733 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn.h>
17 #include "demo.h"
18 
19 #include "../Common/FsSampleCommon.h"
20 #include "FsSampleStreaming.h"
21 
22 using namespace nn;
23 
24 namespace sample { namespace fs { namespace streaming {
25 
26 class RealTimeFileWriter
27 {
28     friend void WriteThreadFunc(RealTimeFileWriter *stream);
29 private:
30     nn::fs::FileOutputStream m_File;
31     s32 m_CurrentBufferIndex;
32     bit8 m_pBuffers[2][BUFFER_SIZE] NN_ATTRIBUTE_ALIGN(4);
33     nn::os::Thread m_Thread;
34     nn::os::BlockingQueue m_ReceiveQueue;
35     nn::os::BlockingQueue m_ReturnQueue;
36     uptr m_ReceiveQueueBuffer[BUFFER_SIZE / MEMO_BUFFER_SIZE * 2 + 1]; // Because you must enter NULL for the end flag, a queue size that is one larger than the number of buffers is required.
37     uptr m_ReturnQueueBuffer[BUFFER_SIZE / MEMO_BUFFER_SIZE * 2 + 1];  // Because you must enter NULL for the end flag, a queue size that is one larger than the number of buffers is required.
38     s32 m_ReturnedNum; // Total number of packets transferred to the main thread.
39     bool m_IsInitialized;
40     NN_PADDING3;
41     NN_PADDING4;
42     wchar_t m_Path[MAX_PATH_LENGTH + 1];
43     s64 m_WrittenSize; // Number of frames written to a file * number of bytes per frame
44 
45     void CreatePacket();
46     void ThreadFunc();
47 
48     nn::Result InitializeFile();
49     nn::Result FinalizeFile();
50 
51 public:
RealTimeFileWriter()52     RealTimeFileWriter() :
53         m_IsInitialized(false)
54     {
55         m_Path[MAX_PATH_LENGTH] = L'\0';
56     }
57     nn::Result Start(const wchar_t *path);
58     nn::Result End();
59     ENUM_RECORDING_RESULT SetData(const bit8 *pOut, s32 size);
60 };
61 
62 static RealTimeFileWriter s_RealTimeWriter;
63 
WriteThreadFunc(RealTimeFileWriter * writer)64 void WriteThreadFunc(RealTimeFileWriter *writer)
65 {
66     writer->ThreadFunc();
67 }
68 
Start(const wchar_t * path)69 Result RealTimeFileWriter::Start(const wchar_t *path)
70 {
71     if (m_IsInitialized == true)
72     {
73         return nn::Result(nn::Result::LEVEL_USAGE, nn::Result::SUMMARY_INVALID_STATE, nn::Result::MODULE_COMMON, nn::Result::DESCRIPTION_ALREADY_INITIALIZED);
74     }
75 
76     // Initializes the send/receive queue.
77     m_ReceiveQueue.Initialize(m_ReceiveQueueBuffer, sizeof(m_ReceiveQueueBuffer) / sizeof(m_ReceiveQueueBuffer[0]));
78     m_ReturnQueue.Initialize(m_ReturnQueueBuffer, sizeof(m_ReturnQueueBuffer) / sizeof(m_ReturnQueueBuffer[0]));
79 
80     m_CurrentBufferIndex = 0;
81     m_ReturnedNum = 0;
82 
83     // Creates a packet structure.
84     CreatePacket();
85 
86     std::wcsncpy(m_Path, path, MAX_PATH_LENGTH);
87 
88     // The amount of time to create a file differs significantly according to the SD, so there is no guarantee for realtime characteristics.
89     //
90     // When file creation is made to happen in a write thread and capturing to the buffer is started in the background, if a slow SD is executed, then a frame that should be written is dropped.
91     //
92     //
93     // This sample creates file with the main thread, and during this period no capturing to the buffer is started.
94     // To start capture immediately after calling Start, create the file in advance or take some other measure.
95 
96     RETURN_IF_FAILED(nn::fs::TryCreateFile(m_Path, sizeof(Header) + MAX_MEMO_FILE_SIZE));
97 
98     // Starts the thread with a higher priority than this thread as the thread for writing.
99     RETURN_IF_FAILED(m_Thread.TryStartUsingAutoStack(WriteThreadFunc, this, 4 * 1024, nn::os::Thread::GetCurrentPriority() - 1));
100 
101     m_IsInitialized = true;
102 
103     return ResultSuccess();
104 }
105 
106 // Initialize a packet structure.
CreatePacket()107 void RealTimeFileWriter::CreatePacket()
108 {
109     for (int j = 0; j < 2; ++j)
110     {
111         bit8 *ptr = m_pBuffers[j];
112         for (int i = 0; i < BUFFER_SIZE / MEMO_BUFFER_SIZE; ++i)
113         {
114             PacketData *packet = new PacketData(ptr, MEMO_BUFFER_SIZE, m_ReceiveQueue);
115 
116             // Sends to the empty packet queue.
117             m_ReturnQueue.Enqueue(reinterpret_cast<uptr>(packet));
118             m_ReturnedNum++;
119             ptr += MEMO_BUFFER_SIZE;
120         }
121     }
122 }
123 
End()124 Result RealTimeFileWriter::End()
125 {
126     // Sends a NULL packet indicating recording has ended.
127     m_ReceiveQueue.Enqueue(NULL);
128 
129     m_Thread.Join();
130     m_Thread.Finalize();
131 
132     // Empty packets are acquired and structures destroyed until the NULL packet is received.
133     while (uptr ptr = m_ReturnQueue.Dequeue())
134     {
135         delete reinterpret_cast<PacketData*>(ptr);
136     }
137 
138     m_IsInitialized = false;
139     return ResultSuccess();
140 }
141 
InitializeFile()142 Result RealTimeFileWriter::InitializeFile()
143 {
144     // Opens the target file and sets the priority to one that is realtime.
145     RETURN_IF_FAILED(m_File.TryInitialize(m_Path, false));
146     RETURN_IF_FAILED_1(m_File.TrySetPriority(nn::fs::PRIORITY_APP_REALTIME),
147         m_File.Finalize());
148 
149     // Seeks to part of the region where the header is saved.
150     RETURN_IF_FAILED_1(m_File.TrySeek(sizeof(Header), nn::fs::POSITION_BASE_BEGIN),
151         m_File.Finalize());
152 
153     return ResultSuccess();
154 }
155 
FinalizeFile()156 Result RealTimeFileWriter::FinalizeFile()
157 {
158     // Because the file size cannot be changed with expanded save data, the length of the written streaming is saved in the header.
159     Header header;
160     header.size = m_WrittenSize;
161     RETURN_IF_FAILED_1(m_File.TrySeek(0, nn::fs::POSITION_BASE_BEGIN),
162         m_File.Finalize());
163     s32 size;
164     // Flushes while writing.
165     RETURN_IF_FAILED_1(m_File.TryWrite(&size, &header, sizeof(header), true),
166         m_File.Finalize());
167 
168     m_File.Finalize();
169 
170     return ResultSuccess();
171 }
172 
173 // Thread function for writing.
ThreadFunc()174 void RealTimeFileWriter::ThreadFunc()
175 {
176     NN_ERR_THROW_FATAL_ALL(InitializeFile());
177     NN_TASSERT_(m_ReturnedNum * MEMO_BUFFER_SIZE <= MAX_MEMO_FILE_SIZE);
178 
179     s32 maxReturnNum = MAX_MEMO_FILE_SIZE / MEMO_BUFFER_SIZE;
180     m_WrittenSize = 0;
181 
182     bool continueQueue = true;
183     while (continueQueue)
184     {
185         int writePacketNum;
186         uptr packet[BUFFER_SIZE / MEMO_BUFFER_SIZE];
187 
188         // Gets the data received in the written data reception queue.
189         // Continues to acquire until the total size of the acquired data reaches the size that was written in batch.
190         for (writePacketNum = 0; writePacketNum < sizeof(packet) / sizeof(packet[0]); ++writePacketNum)
191         {
192             packet[writePacketNum] = m_ReceiveQueue.Dequeue();
193 
194             // Omitted when the NULL packet indicating the end of recording is received.
195             if (packet[writePacketNum] == NULL)
196             {
197                 continueQueue = false;
198                 break;
199             }
200         }
201 
202         if (writePacketNum > 0)
203         {
204             s32 size;
205             // Writes the received data.
206             NN_ERR_THROW_FATAL_ALL(m_File.TryWrite(&size, reinterpret_cast<PacketData*>(packet[0])->GetData(), writePacketNum * MEMO_BUFFER_SIZE, false));
207             m_WrittenSize += size;
208         }
209 
210         // Sends the packet that has been written to the empty packet queue.
211         // Controls so that the total size sent to the empty packet queue does not exceed the file size.
212         int returnNum = (writePacketNum + m_ReturnedNum < maxReturnNum) ? writePacketNum : maxReturnNum - m_ReturnedNum;
213         for (int i = 0; i < returnNum; ++i)
214         {
215             m_ReturnQueue.Enqueue(packet[i]);
216         }
217 
218         m_ReturnedNum += returnNum;
219         if (m_ReturnedNum >= maxReturnNum)
220         {
221             // On the main thread side, because the file is full, sends a NULL packet to notify not to write any more after this packet.
222             //
223             m_ReturnQueue.Enqueue(NULL);
224         }
225     }
226 
227     NN_ERR_THROW_FATAL_ALL(FinalizeFile());
228 
229     // Packs a NULL packet for the flag during finalization.
230     m_ReturnQueue.Enqueue(NULL);
231 }
232 
SetData(const bit8 * data,s32 size)233 ENUM_RECORDING_RESULT RealTimeFileWriter::SetData(const bit8 *data, s32 size)
234 {
235     // Waits for an empty packet to be sent from the write thread and gets the one that is received.
236     uptr out;
237     if (!m_ReturnQueue.TryDequeue(&out))
238     {
239         return RECORDING_RESULT_BUFFER_FULL;
240     }
241 
242     if (out == NULL)
243     {
244         return RECORDING_RESULT_FILE_FULL;
245     }
246 
247     PacketData *packet = reinterpret_cast<PacketData*>(out);
248 
249     NN_PANIC_IF_FALSE(size == packet->GetSize());
250 
251     // Copies data to the empty packet.
252     std::memcpy(packet->GetData(), data, size);
253 
254     // Sends to the receive queue for the write thread.
255     m_ReceiveQueue.Enqueue(reinterpret_cast<uptr>(packet));
256 
257     return RECORDING_RESULT_SUCCESS;
258 }
259 
260 // Sends data to the recording buffer.
261 // Returns RECORDING_RESULT_BUFFER_FULL when there is no free buffer due to a reason such as too much time was taken for saving; returns RECORDING_RESULT_FILE_FULL when the file for recording is full.
262 //
SaveMemo(const bit8 * srcBuffer)263 ENUM_RECORDING_RESULT SaveMemo(const bit8 *srcBuffer)
264 {
265     return s_RealTimeWriter.SetData(srcBuffer, MEMO_BUFFER_SIZE);
266 }
267 
268 // Starts recording.
StartRecording(const wchar_t * fileName)269 Result StartRecording(const wchar_t *fileName)
270 {
271     RETURN_IF_FAILED(s_RealTimeWriter.Start(fileName));
272 
273     return ResultSuccess();
274 }
275 
276 // Ends recording.
StopRecording()277 Result StopRecording()
278 {
279     RETURN_IF_FAILED(s_RealTimeWriter.End());
280 
281     return ResultSuccess();
282 }
283 
284 }}}
285