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