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.
5 // Native Client audio/video
7 // Package av implements audio and video access for Native Client
8 // binaries running standalone or embedded in a web browser window.
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
23 var srpcEnabled = srpc.Enabled()
25 // native_client/src/trusted/service_runtime/include/sys/audio_video.h
27 // Subsystem values for Init.
29 SubsystemVideo = 1 << iota
33 // SubsystemRawEvents;
37 AudioFormatStereo44K = iota
41 // A Window represents a connection to the Native Client window.
42 // It implements draw.Context.
44 Embedded bool // running as part of a web page?
45 *Image // screen image
46 eventc chan interface{}
49 // *Window implements draw.Window.
50 var _ draw.Window = (*Window)(nil)
52 func (w *Window) EventChan() <-chan interface{} { return w.eventc }
54 func (w *Window) Close() os.Error {
55 // TODO(nigeltao): implement.
59 func (w *Window) Screen() draw.Image { return w.Image }
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.
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.
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) {
76 xsubsys &^= SubsystemVideo | SubsystemEmbed
79 if xsubsys&SubsystemEmbed != 0 {
80 return nil, os.NewError("not embedded")
84 err := multimediaInit(xsubsys)
89 if subsys&SubsystemVideo != 0 {
90 if dx, dy, err = videoInit(dx, dy); err != nil {
93 w.Image = newImage(dx, dy, bridge.pixel)
94 w.eventc = make(chan interface{}, 64)
97 if subsys&SubsystemAudio != 0 {
99 if n, err = audioInit(AudioFormatStereo44K, 2048); err != nil {
105 if subsys&SubsystemVideo != 0 {
112 func (w *Window) FlushImage() {
116 videoUpdate(w.Image.Linear)
119 func multimediaInit(subsys int) (err os.Error) {
120 return os.NewSyscallError("multimedia_init", syscall.MultimediaInit(subsys))
123 func videoInit(dx, dy int) (ndx, ndy int, err os.Error) {
125 bridge.share.ready = 1
126 return int(bridge.share.width), int(bridge.share.height), nil
128 if e := syscall.VideoInit(dx, dy); e != 0 {
129 return 0, 0, os.NewSyscallError("video_init", int(e))
134 func videoUpdate(data []Color) (err os.Error) {
136 bridge.flushRPC.Call("upcall", nil)
139 return os.NewSyscallError("video_update", syscall.VideoUpdate((*uint32)(&data[0])))
142 var noEvents = os.NewError("no events")
144 func videoPollEvent(ev []byte) (err os.Error) {
146 r := bridge.share.eq.ri
147 if r == bridge.share.eq.wi {
150 copy(ev, bridge.share.eq.event[r][0:])
151 bridge.share.eq.ri = (r + 1) % eqsize
154 return os.NewSyscallError("video_poll_event", syscall.VideoPollEvent(&ev[0]))
157 func audioInit(fmt int, want int) (got int, err os.Error) {
159 e := syscall.AudioInit(fmt, want, &x)
163 return 0, os.NewSyscallError("audio_init", e)
166 var audioSize uintptr
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.
174 // To find out the initial number of samples to write, call AudioStream(nil).
176 func AudioStream(data []uint16) (nextSize int, err os.Error) {
178 e := os.NewSyscallError("audio_stream", syscall.AudioStream(nil, &audioSize))
179 return int(audioSize), e
182 return int(audioSize), nil
184 if uintptr(len(data))*2 != audioSize {
185 log.Printf("invalid audio size want %d got %d", audioSize, len(data))
187 e := os.NewSyscallError("audio_stream", syscall.AudioStream(&data[0], &audioSize))
188 return int(audioSize), e
191 // Synchronization structure to wait for bridge to become ready.
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 }
211 // Data structure shared with host via mmap.
212 type videoShare struct {
213 revision int32 // definition below is rev 100 unless noted
218 ri uint32 // read index [0,eqsize)
219 wi uint32 // write index [0,eqsize)
221 event [eqsize][64]byte
227 // video backing store information
228 width, height, _, size int32
229 ready int32 // rev 0x101
232 // The frame buffer data is videoShareSize bytes after
233 // the videoShare begins.
234 const videoShareSize = 16 * 1024
236 type multimediaBridge struct{}
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)
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))
250 addr, _, errno := syscall.Syscall6(syscall.SYS_MMAP,
253 syscall.PROT_READ|syscall.PROT_WRITE,
255 uintptr(bridge.displayFd),
258 log.Exitf("mmap display: %s", os.Errno(errno))
261 bridge.share = (*videoShare)(unsafe.Pointer(addr))
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]
269 // Configure RPC connection back to client.
271 bridge.client, err = srpc.NewClient(bridge.rpcFd)
273 log.Exitf("NewClient: %s", err)
275 bridge.flushRPC = bridge.client.NewRPC(nil)
277 // Notify waiters that the bridge is ready.
278 println("bridged", bridge.share.revision)
285 bridge.c = make(chan bool, 1)
287 srpc.Add("nacl_multimedia_bridge", "hh:", multimediaBridge{})