OSDN Git Service

Add Go frontend, libgo library, and Go testsuite.
[pf3gnuchains/gcc-fork.git] / libgo / go / exp / nacl / av / av.go
1 // Copyright 2009 The Go Authors.  All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Native Client audio/video
6
7 // Package av implements audio and video access for Native Client
8 // binaries running standalone or embedded in a web browser window.
9 //
10 // The C version of the API is documented at
11 // http://nativeclient.googlecode.com/svn/data/docs_tarball/nacl/googleclient/native_client/scons-out/doc/html/group__audio__video.html
12 package av
13
14 import (
15         "exp/draw"
16         "exp/nacl/srpc"
17         "log"
18         "os"
19         "syscall"
20         "unsafe"
21 )
22
23 var srpcEnabled = srpc.Enabled()
24
25 // native_client/src/trusted/service_runtime/include/sys/audio_video.h
26
27 // Subsystem values for Init.
28 const (
29         SubsystemVideo = 1 << iota
30         SubsystemAudio
31         SubsystemEmbed
32 )
33 //      SubsystemRawEvents;
34
35 // Audio formats.
36 const (
37         AudioFormatStereo44K = iota
38         AudioFormatStereo48K
39 )
40
41 // A Window represents a connection to the Native Client window.
42 // It implements draw.Context.
43 type Window struct {
44         Embedded bool // running as part of a web page?
45         *Image        // screen image
46         eventc   chan interface{}
47 }
48
49 // *Window implements draw.Window.
50 var _ draw.Window = (*Window)(nil)
51
52 func (w *Window) EventChan() <-chan interface{} { return w.eventc }
53
54 func (w *Window) Close() os.Error {
55         // TODO(nigeltao): implement.
56         return nil
57 }
58
59 func (w *Window) Screen() draw.Image { return w.Image }
60
61 // Init initializes the Native Client subsystems specified by subsys.
62 // Init must be called before using any of the other functions
63 // in this package, and it must be called only once.
64 //
65 // If the SubsystemVideo flag is set, Init requests a window of size dx×dy.
66 // When embedded in a web page, the web page's window specification
67 // overrides the parameters to Init, so the returned Window may have
68 // a different size than requested.
69 //
70 // If the SubsystemAudio flag is set, Init requests a connection to the
71 // audio device carrying 44 kHz 16-bit stereo PCM audio samples.
72 func Init(subsys int, dx, dy int) (*Window, os.Error) {
73         xsubsys := subsys
74         if srpcEnabled {
75                 waitBridge()
76                 xsubsys &^= SubsystemVideo | SubsystemEmbed
77         }
78
79         if xsubsys&SubsystemEmbed != 0 {
80                 return nil, os.NewError("not embedded")
81         }
82
83         w := new(Window)
84         err := multimediaInit(xsubsys)
85         if err != nil {
86                 return nil, err
87         }
88
89         if subsys&SubsystemVideo != 0 {
90                 if dx, dy, err = videoInit(dx, dy); err != nil {
91                         return nil, err
92                 }
93                 w.Image = newImage(dx, dy, bridge.pixel)
94                 w.eventc = make(chan interface{}, 64)
95         }
96
97         if subsys&SubsystemAudio != 0 {
98                 var n int
99                 if n, err = audioInit(AudioFormatStereo44K, 2048); err != nil {
100                         return nil, err
101                 }
102                 println("audio", n)
103         }
104
105         if subsys&SubsystemVideo != 0 {
106                 go w.readEvents()
107         }
108
109         return w, nil
110 }
111
112 func (w *Window) FlushImage() {
113         if w.Image == nil {
114                 return
115         }
116         videoUpdate(w.Image.Linear)
117 }
118
119 func multimediaInit(subsys int) (err os.Error) {
120         return os.NewSyscallError("multimedia_init", syscall.MultimediaInit(subsys))
121 }
122
123 func videoInit(dx, dy int) (ndx, ndy int, err os.Error) {
124         if srpcEnabled {
125                 bridge.share.ready = 1
126                 return int(bridge.share.width), int(bridge.share.height), nil
127         }
128         if e := syscall.VideoInit(dx, dy); e != 0 {
129                 return 0, 0, os.NewSyscallError("video_init", int(e))
130         }
131         return dx, dy, nil
132 }
133
134 func videoUpdate(data []Color) (err os.Error) {
135         if srpcEnabled {
136                 bridge.flushRPC.Call("upcall", nil)
137                 return
138         }
139         return os.NewSyscallError("video_update", syscall.VideoUpdate((*uint32)(&data[0])))
140 }
141
142 var noEvents = os.NewError("no events")
143
144 func videoPollEvent(ev []byte) (err os.Error) {
145         if srpcEnabled {
146                 r := bridge.share.eq.ri
147                 if r == bridge.share.eq.wi {
148                         return noEvents
149                 }
150                 copy(ev, bridge.share.eq.event[r][0:])
151                 bridge.share.eq.ri = (r + 1) % eqsize
152                 return nil
153         }
154         return os.NewSyscallError("video_poll_event", syscall.VideoPollEvent(&ev[0]))
155 }
156
157 func audioInit(fmt int, want int) (got int, err os.Error) {
158         var x int
159         e := syscall.AudioInit(fmt, want, &x)
160         if e == 0 {
161                 return x, nil
162         }
163         return 0, os.NewSyscallError("audio_init", e)
164 }
165
166 var audioSize uintptr
167
168 // AudioStream provides access to the audio device.
169 // Each call to AudioStream writes the given data,
170 // which should be a slice of 16-bit stereo PCM audio samples,
171 // and returns the number of samples required by the next
172 // call to AudioStream.
173 //
174 // To find out the initial number of samples to write, call AudioStream(nil).
175 //
176 func AudioStream(data []uint16) (nextSize int, err os.Error) {
177         if audioSize == 0 {
178                 e := os.NewSyscallError("audio_stream", syscall.AudioStream(nil, &audioSize))
179                 return int(audioSize), e
180         }
181         if data == nil {
182                 return int(audioSize), nil
183         }
184         if uintptr(len(data))*2 != audioSize {
185                 log.Printf("invalid audio size want %d got %d", audioSize, len(data))
186         }
187         e := os.NewSyscallError("audio_stream", syscall.AudioStream(&data[0], &audioSize))
188         return int(audioSize), e
189 }
190
191 // Synchronization structure to wait for bridge to become ready.
192 var bridge struct {
193         c         chan bool
194         displayFd int
195         rpcFd     int
196         share     *videoShare
197         pixel     []Color
198         client    *srpc.Client
199         flushRPC  *srpc.RPC
200 }
201
202 // Wait for bridge to become ready.
203 // When chan is first created, there is nothing in it,
204 // so this blocks.  Once the bridge is ready, multimediaBridge.Run
205 // will drop a value into the channel.  Then any calls
206 // to waitBridge will finish, taking the value out and immediately putting it back.
207 func waitBridge() { bridge.c <- <-bridge.c }
208
209 const eqsize = 64
210
211 // Data structure shared with host via mmap.
212 type videoShare struct {
213         revision int32 // definition below is rev 100 unless noted
214         mapSize  int32
215
216         // event queue
217         eq struct {
218                 ri    uint32 // read index [0,eqsize)
219                 wi    uint32 // write index [0,eqsize)
220                 eof   int32
221                 event [eqsize][64]byte
222         }
223
224         // now unused
225         _, _, _, _ int32
226
227         // video backing store information
228         width, height, _, size int32
229         ready                  int32 // rev 0x101
230 }
231
232 // The frame buffer data is videoShareSize bytes after
233 // the videoShare begins.
234 const videoShareSize = 16 * 1024
235
236 type multimediaBridge struct{}
237
238 // If using SRPC, the runtime will call this method to pass in two file descriptors,
239 // one to mmap to get the display memory, and another to use for SRPCs back
240 // to the main process.
241 func (multimediaBridge) Run(arg, ret []interface{}, size []int) srpc.Errno {
242         bridge.displayFd = arg[0].(int)
243         bridge.rpcFd = arg[1].(int)
244
245         var st syscall.Stat_t
246         if errno := syscall.Fstat(bridge.displayFd, &st); errno != 0 {
247                 log.Exitf("mmbridge stat display: %s", os.Errno(errno))
248         }
249
250         addr, _, errno := syscall.Syscall6(syscall.SYS_MMAP,
251                 0,
252                 uintptr(st.Size),
253                 syscall.PROT_READ|syscall.PROT_WRITE,
254                 syscall.MAP_SHARED,
255                 uintptr(bridge.displayFd),
256                 0)
257         if errno != 0 {
258                 log.Exitf("mmap display: %s", os.Errno(errno))
259         }
260
261         bridge.share = (*videoShare)(unsafe.Pointer(addr))
262
263         // Overestimate frame buffer size
264         // (must use a compile-time constant)
265         // and then reslice.  256 megapixels (1 GB) should be enough.
266         fb := (*[256 * 1024 * 1024]Color)(unsafe.Pointer(addr + videoShareSize))
267         bridge.pixel = fb[0 : (st.Size-videoShareSize)/4]
268
269         // Configure RPC connection back to client.
270         var err os.Error
271         bridge.client, err = srpc.NewClient(bridge.rpcFd)
272         if err != nil {
273                 log.Exitf("NewClient: %s", err)
274         }
275         bridge.flushRPC = bridge.client.NewRPC(nil)
276
277         // Notify waiters that the bridge is ready.
278         println("bridged", bridge.share.revision)
279         bridge.c <- true
280
281         return srpc.OK
282 }
283
284 func init() {
285         bridge.c = make(chan bool, 1)
286         if srpcEnabled {
287                 srpc.Add("nacl_multimedia_bridge", "hh:", multimediaBridge{})
288         }
289 }