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