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: 46467 $
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 RealTimeFileReader
27 {
28 friend void ReadThreadFunc(RealTimeFileReader *stream);
29 private:
30 nn::fs::FileInputStream m_File;
31 s32 m_CurrentBufferIndex;
32 bit8 m_pBuffers[2][BUFFER_SIZE] NN_ATTRIBUTE_ALIGN(4);
33 s32 m_PacketNum[2];
34 nn::os::Thread m_Thread;
35 nn::os::BlockingQueue m_SendQueue;
36 nn::os::BlockingQueue m_ReturnQueue;
37 uptr m_SendQueueBuffer[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 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.
39 bool m_IsInitialized;
40 bool m_Aborted;
41 NN_PADDING2;
42 wchar_t m_Path[MAX_PATH_LENGTH + 1];
43 s64 m_FileRestSize;
44
45 bool RefillBuffer();
46 void ThreadFunc();
47
48 nn::Result InitializeFile();
49 nn::Result FinalizeFile();
50
51 public:
RealTimeFileReader()52 RealTimeFileReader() :
53 m_IsInitialized(false)
54 {
55 m_Path[MAX_PATH_LENGTH] = L'\0';
56 }
57
58 nn::Result Start(const wchar_t *path);
59 nn::Result End();
60 void Abort();
61 bool TakePacket(PacketData **pOut);
62 };
63
64 static RealTimeFileReader s_RealTimeReader;
65
ReadThreadFunc(RealTimeFileReader * reader)66 void ReadThreadFunc(RealTimeFileReader *reader)
67 {
68 reader->ThreadFunc();
69 }
70
Start(const wchar_t * path)71 Result RealTimeFileReader::Start(const wchar_t *path)
72 {
73 if (m_IsInitialized == true)
74 {
75 return nn::Result(nn::Result::LEVEL_USAGE, nn::Result::SUMMARY_INVALID_STATE, nn::Result::MODULE_COMMON, nn::Result::DESCRIPTION_ALREADY_INITIALIZED);
76 }
77
78 // Initializes the send/receive queue.
79 m_SendQueue.Initialize(m_SendQueueBuffer, sizeof(m_SendQueueBuffer) / sizeof(m_SendQueueBuffer[0]));
80 m_ReturnQueue.Initialize(m_ReturnQueueBuffer, sizeof(m_ReturnQueueBuffer) / sizeof(m_ReturnQueueBuffer[0]));
81
82 m_CurrentBufferIndex = 0;
83 m_PacketNum[0] = 0;
84 m_PacketNum[1] = 0;
85
86 std::wcsncpy(m_Path, path, MAX_PATH_LENGTH);
87
88 m_Aborted = false;
89 // Starts the thread with a higher priority than this thread as the thread for loading.
90 RETURN_IF_FAILED_1(m_Thread.TryStartUsingAutoStack(ReadThreadFunc, this, 4 * 1024, nn::os::Thread::GetCurrentPriority() - 1),
91 m_File.Finalize());
92
93 m_IsInitialized = true;
94
95 return ResultSuccess();
96 }
97
End()98 Result RealTimeFileReader::End()
99 {
100 NN_PANIC_IF_FALSE(m_PacketNum[m_CurrentBufferIndex] == 0 && m_PacketNum[1 - m_CurrentBufferIndex] == 0);
101 m_Thread.Join();
102 m_Thread.Finalize();
103 m_IsInitialized = false;
104 return ResultSuccess();
105 }
106
107 // Loads the front buffer.
108 // Returns false if already finished loading up to the end of the file.
RefillBuffer()109 bool RealTimeFileReader::RefillBuffer()
110 {
111 s32 size;
112
113 if (m_FileRestSize <= 0)
114 {
115 return false;
116 }
117
118 // Shortens the load size if the load size is larger than the length of the saved streaming.
119 s32 readSize = (m_FileRestSize < BUFFER_SIZE) ? m_FileRestSize : BUFFER_SIZE;
120 NN_ERR_THROW_FATAL_ALL(m_File.TryRead(&size, m_pBuffers[m_CurrentBufferIndex], readSize));
121
122 m_FileRestSize -= size;
123
124 bit8 *ptr = m_pBuffers[m_CurrentBufferIndex];
125 for (int i = 0; i < size / MEMO_BUFFER_SIZE; ++i)
126 {
127 // Segments data loaded in batch into packets.
128 PacketData *packet = new PacketData(ptr, MEMO_BUFFER_SIZE, m_ReturnQueue);
129 NN_PANIC_IF_NULL(packet);
130
131 // Sends packets to the send queue.
132 m_SendQueue.Enqueue(reinterpret_cast<uptr>(packet));
133 ptr += packet->GetSize();
134 m_PacketNum[m_CurrentBufferIndex]++;
135 }
136
137 return true;
138 }
139
InitializeFile()140 Result RealTimeFileReader::InitializeFile()
141 {
142 // Opens the target file and sets the priority to one that is realtime.
143 RETURN_IF_FAILED(m_File.TryInitialize(m_Path));
144 RETURN_IF_FAILED_1(m_File.TrySetPriority(nn::fs::PRIORITY_APP_REALTIME),
145 m_File.Finalize());
146 s32 size;
147 Header header;
148 RETURN_IF_FAILED_1(m_File.TryRead(&size, &header, sizeof(header)),
149 m_File.Finalize());
150 m_FileRestSize = header.size;
151
152 return ResultSuccess();
153 }
154
FinalizeFile()155 Result RealTimeFileReader::FinalizeFile()
156 {
157 m_File.Finalize();
158 return ResultSuccess();
159 }
160
161 // Thread function for loading.
ThreadFunc()162 void RealTimeFileReader::ThreadFunc()
163 {
164 NN_ERR_THROW_FATAL_ALL(InitializeFile());
165
166 bool continuePacket = true;
167 while (continuePacket)
168 {
169 if (m_Aborted || !RefillBuffer())
170 {
171 // When playback is stopped or if the end of the file has been loaded, send a NULL packet indicating the end packet.
172 m_SendQueue.Enqueue(NULL);
173 continuePacket = false;
174 }
175
176 m_CurrentBufferIndex = 1 - m_CurrentBufferIndex;
177 // Wait for all front buffers to be used.
178 while (m_PacketNum[m_CurrentBufferIndex])
179 {
180 PacketData *packet = reinterpret_cast<PacketData*>(m_ReturnQueue.Dequeue());
181 delete packet;
182 m_PacketNum[m_CurrentBufferIndex]--;
183 }
184 }
185
186 NN_ERR_THROW_FATAL_ALL(FinalizeFile());
187 }
188
189 // Gets the loaded packets.
190 // Returns false if the packet has not reached the buffer.
TakePacket(PacketData ** pOut)191 bool RealTimeFileReader::TakePacket(PacketData **pOut)
192 {
193 // PacketData::Free() must be called for obtained packets when their use is finished.
194 // This is so that the next loading with the load thread can be started by calling PacketData::Free(), this packet is sent to the empty packet queue as a used packet and having a sufficient amount of empty packets accumulate.
195 //
196 uptr out;
197 if (!m_SendQueue.TryDequeue(&out))
198 {
199 return false;
200 }
201 *pOut = reinterpret_cast<PacketData*>(out);
202
203 return true;
204 }
205
Abort()206 void RealTimeFileReader::Abort()
207 {
208 m_Aborted = true;
209 while (uptr ptr = m_SendQueue.Dequeue())
210 {
211 reinterpret_cast<PacketData*>(ptr)->Free();
212 }
213 }
214
215 // Gets the packet that includes the pointer to the start of the data from the playback buffer.
216 // Returns false when there is no data for reasons such as loading took a lot of time.
217 // For the obtained packet, please return by calling PacketData::Free() when finished using it.
LoadMemo(PacketData ** pOut)218 bool LoadMemo(PacketData **pOut)
219 {
220 return s_RealTimeReader.TakePacket(pOut);
221 }
222
223 // Start playback.
StartPlaying(const wchar_t * fileName)224 Result StartPlaying(const wchar_t *fileName)
225 {
226 RETURN_IF_FAILED(s_RealTimeReader.Start(fileName));
227
228 return ResultSuccess();
229 }
230
231 // Stop playback midway and end.
StopPlaying()232 Result StopPlaying()
233 {
234 s_RealTimeReader.Abort();
235 return s_RealTimeReader.End();
236 }
237
238 // End when playback is goes to the end.
FinishPlaying()239 Result FinishPlaying()
240 {
241 return s_RealTimeReader.End();
242 }
243
244 }}}
245