Commit cbd40daf authored by Russ Cox's avatar Russ Cox

final Native Client package - av

R=r
DELTA=894  (887 added, 0 deleted, 7 changed)
OCL=35115
CL=35286
parent 433e0597
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.$(GOARCH)
TARG=nacl/av
GOFILES=\
av.go\
event.go\
image.go\
include $(GOROOT)/src/Make.pkg
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Native Client audio/video
// Package av implements audio and video access for Native Client
// binaries running standalone or embedded in a web browser window.
package av
import (
"bytes";
"draw";
"log";
"nacl/srpc";
"os";
"syscall";
"unsafe";
)
var srpcEnabled = srpc.Enabled();
// native_client/src/trusted/service_runtime/include/sys/audio_video.h
// Subsystem values for Init.
const (
SubsystemVideo = 1<<iota;
SubsystemAudio;
SubsystemEmbed;
)
// SubsystemRawEvents;
// Audio formats.
const (
AudioFormatStereo44K = iota;
AudioFormatStereo48K;
)
// A Window represents a connection to the Native Client window.
// It implements draw.Context.
type Window struct {
Embedded bool; // running as part of a web page?
*Image; // screen image
mousec chan draw.Mouse;
kbdc chan int;
quitc chan bool;
resizec chan bool;
}
// *Window implements draw.Context
var _ draw.Context = (*Window)(nil)
func (w *Window) KeyboardChan() <-chan int {
return w.kbdc;
}
func (w *Window) MouseChan() <-chan draw.Mouse {
return w.mousec;
}
func (w *Window) QuitChan() <-chan bool {
return w.quitc;
}
func (w *Window) ResizeChan() <-chan bool {
return w.resizec;
}
func (w *Window) Screen() draw.Image {
return w.Image;
}
// Init initializes the Native Client subsystems specified by subsys.
// Init must be called before using any of the other functions
// in this package, and it must be called only once.
//
// If the SubsystemVideo flag is set, Init requests a window of size dx×dy.
// When embedded in a web page, the web page's window specification
// overrides the parameters to Init, so the returned Window may have
// a different size than requested.
//
// If the SubsystemAudio flag is set, Init requests a connection to the
// audio device carrying 44 kHz 16-bit stereo PCM audio samples.
func Init(subsys int, dx, dy int) (*Window, os.Error) {
xsubsys := subsys;
if srpcEnabled {
waitBridge();
xsubsys &^= SubsystemVideo|SubsystemEmbed;
}
if xsubsys & SubsystemEmbed != 0 {
return nil, os.NewError("not embedded");
}
w := new(Window);
err := multimediaInit(xsubsys);
if err != nil {
return nil, err;
}
if subsys&SubsystemVideo != 0 {
if dx, dy, err = videoInit(dx, dy); err != nil {
return nil, err;
}
w.Image = newImage(dx, dy, bridge.pixel);
w.resizec = make(chan bool, 64);
w.kbdc = make(chan int, 64);
w.mousec = make(chan draw.Mouse, 64);
w.quitc = make(chan bool);
}
if subsys&SubsystemAudio != 0 {
var n int;
if n, err = audioInit(AudioFormatStereo44K, 2048); err != nil {
return nil, err;
}
println("audio", n);
}
if subsys&SubsystemVideo != 0 {
go w.readEvents();
}
return w, nil;
}
func (w *Window) FlushImage() {
if w.Image == nil {
return;
}
videoUpdate(w.Image.Linear);
}
func multimediaInit(subsys int) (err os.Error) {
return os.NewSyscallError("multimedia_init", syscall.MultimediaInit(subsys));
}
func videoInit(dx, dy int) (ndx, ndy int, err os.Error) {
if srpcEnabled {
bridge.share.ready = 1;
return int(bridge.share.width), int(bridge.share.height), nil;
}
if e := syscall.VideoInit(dx, dy); e != 0 {
return 0, 0, os.NewSyscallError("video_init", int(e));
}
return dx, dy, nil;
}
func videoUpdate(data []Color) (err os.Error) {
if srpcEnabled {
bridge.flushRPC.Call("upcall", nil);
return;
}
return os.NewSyscallError("video_update", syscall.VideoUpdate((*uint32)(&data[0])));
}
var noEvents = os.NewError("no events");
func videoPollEvent(ev []byte) (err os.Error) {
if srpcEnabled {
r := bridge.share.eq.ri;
if r == bridge.share.eq.wi {
return noEvents;
}
bytes.Copy(ev, &bridge.share.eq.event[r]);
bridge.share.eq.ri = (r+1) % eqsize;
return nil;
}
return os.NewSyscallError("video_poll_event", syscall.VideoPollEvent(&ev[0]));
}
func audioInit(fmt int, want int) (got int, err os.Error) {
var x int;
e := syscall.AudioInit(fmt, want, &x);
if e == 0 {
return x, nil;
}
return 0, os.NewSyscallError("audio_init", e);
}
var audioSize uintptr
// AudioStream provides access to the audio device.
// Each call to AudioStream writes the given data,
// which should be a slice of 16-bit stereo PCM audio samples,
// and returns the number of samples required by the next
// call to AudioStream.
//
// To find out the initial number of samples to write, call AudioStream(nil).
//
func AudioStream(data []uint16) (nextSize int, err os.Error) {
if audioSize == 0 {
e := os.NewSyscallError("audio_stream", syscall.AudioStream(nil, &audioSize));
return int(audioSize), e;
}
if data == nil {
return int(audioSize), nil;
}
if uintptr(len(data))*2 != audioSize {
log.Stdoutf("invalid audio size want %d got %d", audioSize, len(data));
}
e := os.NewSyscallError("audio_stream", syscall.AudioStream(&data[0], &audioSize));
return int(audioSize), e;
}
// Synchronization structure to wait for bridge to become ready.
var bridge struct {
c chan bool;
displayFd int;
rpcFd int;
share *videoShare;
pixel []Color;
client *srpc.Client;
flushRPC *srpc.RPC;
}
// Wait for bridge to become ready.
// When chan is first created, there is nothing in it,
// so this blocks. Once the bridge is ready, multimediaBridge.Run
// will drop a value into the channel. Then any calls
// to waitBridge will finish, taking the value out and immediately putting it back.
func waitBridge() {
bridge.c <- <-bridge.c;
}
const eqsize = 64;
// Data structure shared with host via mmap.
type videoShare struct {
revision int32; // definition below is rev 100 unless noted
mapSize int32;
// event queue
eq struct {
ri uint32; // read index [0,eqsize)
wi uint32; // write index [0,eqsize)
eof int32;
event [eqsize][64]byte;
};
// now unused
_, _, _, _ int32;
// video backing store information
width, height, _, size int32;
ready int32; // rev 0x101
}
// The frame buffer data is videoShareSize bytes after
// the videoShare begins.
const videoShareSize = 16*1024
type multimediaBridge struct{}
// If using SRPC, the runtime will call this method to pass in two file descriptors,
// one to mmap to get the display memory, and another to use for SRPCs back
// to the main process.
func (multimediaBridge) Run(arg, ret []interface{}, size []int) srpc.Errno {
bridge.displayFd = arg[0].(int);
bridge.rpcFd = arg[1].(int);
var st syscall.Stat_t;
if errno := syscall.Fstat(bridge.displayFd, &st); errno != 0 {
log.Exitf("mmbridge stat display: %s", os.Errno(errno));
}
addr, _, errno := syscall.Syscall6(syscall.SYS_MMAP,
0,
uintptr(st.Size),
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED,
uintptr(bridge.displayFd),
0);
if errno != 0 {
log.Exitf("mmap display: %s", os.Errno(errno));
}
bridge.share = (*videoShare)(unsafe.Pointer(addr));
// Overestimate frame buffer size
// (must use a compile-time constant)
// and then reslice. 256 megapixels (1 GB) should be enough.
fb := (*[256*1024*1024]Color)(unsafe.Pointer(addr+videoShareSize));
bridge.pixel = fb[0:(st.Size - videoShareSize)/4];
// Configure RPC connection back to client.
var err os.Error;
bridge.client, err = srpc.NewClient(bridge.rpcFd);
if err != nil {
log.Exitf("NewClient: %s", err);
}
bridge.flushRPC = bridge.client.NewRPC(nil);
// Notify waiters that the bridge is ready.
println("bridged", bridge.share.revision);
bridge.c <- true;
return srpc.OK;
}
func init() {
bridge.c = make(chan bool, 1);
if srpcEnabled {
srpc.Add("nacl_multimedia_bridge", "hh:", multimediaBridge{});
}
}
This diff is collapsed.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package av
import (
"image";
)
// Native Client image format:
// a single linear array of 32-bit ARGB as packed uint32s.
// An Image represents a Native Client frame buffer.
// The pixels in the image can be accessed as a single
// linear slice or as a two-dimensional slice of slices.
// Image implements image.Image.
type Image struct {
Linear []Color;
Pixel [][]Color;
}
var _ image.Image = (*Image)(nil);
func (m *Image) ColorModel() image.ColorModel {
return ColorModel;
}
func (m *Image) Width() int {
if len(m.Pixel) == 0 {
return 0;
}
return len(m.Pixel[0]);
}
func (m *Image) Height() int {
return len(m.Pixel);
}
func (m *Image) At(x, y int) image.Color {
return m.Pixel[y][x];
}
func (m *Image) Set(x, y int, color image.Color) {
if c, ok := color.(Color); ok {
m.Pixel[y][x] = c;
}
m.Pixel[y][x] = makeColor(color.RGBA());
}
func newImage(dx, dy int, linear []Color) *Image {
if linear == nil {
linear = make([]Color, dx*dy);
}
pix := make([][]Color, dy);
for i := range pix {
pix[i] = linear[dx*i : dx*(i+1)];
}
return &Image{linear, pix};
}
// A Color represents a Native Client color value,
// a 32-bit R, G, B, A value packed as 0xAARRGGBB.
type Color uint32
func (p Color) RGBA() (r, g, b, a uint32) {
x := uint32(p);
a = x>>24;
a |= a<<8;
a |= a<<16;
r = (x>>16) & 0xFF;
r |= r<<8;
r |= r<<16;
g = (x>>8) & 0xFF;
g |= g<<8;
g |= g<<16;
b = x & 0xFF;
b |= b<<8;
b |= b<<16;
return;
}
func makeColor(r, g, b, a uint32) Color {
return Color(a>>24<<24 | r>>24<<16 | g>>24<<8 | b>>24);
}
func toColor(color image.Color) image.Color {
if c, ok := color.(Color); ok {
return c;
}
return makeColor(color.RGBA());
}
// ColorModel is the color model corresponding to the Native Client Color.
var ColorModel = image.ColorModelFunc(toColor);
......@@ -19,10 +19,10 @@ import (
// The explicit interface is a way to attach state.
// A Handler is a handler for an SRPC method.
// It reads arguments from m.Arg, checks m.Size for array limits,
// writes return values to m.Ret, and returns an Errno status code.
// It reads arguments from arg, checks size for array limits,
// writes return values to ret, and returns an Errno status code.
type Handler interface {
Run(m *msg) Errno
Run(arg, ret []interface{}, size []int) Errno
}
type method struct {
......@@ -149,7 +149,7 @@ func serveMsg(m *msg, c chan<- *msg) {
return;
}
m.status = meth.handler.Run(m);
m.status = meth.handler.Run(m.Arg, m.Ret, m.Size);
c <- m;
}
......@@ -183,7 +183,7 @@ func Enabled() bool {
// and their argument formats.
type serviceDiscovery struct{}
func (serviceDiscovery) Run(m *msg) Errno {
func (serviceDiscovery) Run(arg, ret []interface{}, size []int) Errno {
var b bytes.Buffer;
for _, m := range rpcMethod {
b.WriteString(m.name);
......@@ -191,10 +191,10 @@ func (serviceDiscovery) Run(m *msg) Errno {
b.WriteString(m.fmt);
b.WriteByte('\n');
}
if b.Len() > m.Size[0] {
if b.Len() > size[0] {
return ErrNoMemory;
}
m.Ret[0] = b.Bytes();
ret[0] = b.Bytes();
return OK;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment