Merge "android/media_sharing: Add box to enter the v-name to connect to"
diff --git a/go/src/v.io/x/media_sharing/media.vdl b/go/src/v.io/x/media_sharing/media.vdl
index 104b4ea..6ff84b0 100644
--- a/go/src/v.io/x/media_sharing/media.vdl
+++ b/go/src/v.io/x/media_sharing/media.vdl
@@ -9,7 +9,7 @@
  // the given URL.  The server will rely on the ContentType response
  // header it gets when fetching the url to decide how to display
  // the media.
-  DisplayUrl(url string) error
+ DisplayUrl(url string) error
 
  // DisplayBytes will cause the server to display whatever media is
  // sent in the stream.  In the case of audio or movie media, the
diff --git a/go/src/v.io/x/media_sharing/mediaclient/mediaclient.go b/go/src/v.io/x/media_sharing/mediaclient/mediaclient.go
index e3dd976..0c90be8 100644
--- a/go/src/v.io/x/media_sharing/mediaclient/mediaclient.go
+++ b/go/src/v.io/x/media_sharing/mediaclient/mediaclient.go
@@ -6,7 +6,12 @@
 
 import (
 	"fmt"
+	"io"
+	"mime"
+	"net/http"
+	"net/url"
 	"os"
+	"path/filepath"
 
 	"v.io/v23/context"
 	"v.io/x/lib/cmdline"
@@ -15,7 +20,12 @@
 	_ "v.io/x/ref/runtime/factories/static"
 )
 
+var stream *bool
+
 func main() {
+	stream = root.Flags.Bool("stream", true,
+		"If true stream the data instead of sending the URL.")
+
 	cmdline.Main(root)
 }
 
@@ -28,12 +38,62 @@
 	Short:  "Share media with a remote display.",
 }
 
+type streamWriter struct {
+	buf    []byte
+	stream interface {
+		Send(item []byte) error
+		Close() error
+	}
+}
+
+func (w *streamWriter) Write(p []byte) (n int, err error) {
+	return len(p), w.stream.Send(p)
+}
+
 func display(ctx *context.T, env *cmdline.Env, args []string) error {
 	if len(args) < 2 {
 		return fmt.Errorf("Both a server and url must be specified.")
 	}
-	name, url := args[0], args[1]
+	name, urlStr := args[0], args[1]
 	client := media_sharing.MediaSharingClient(name)
 
-	return client.DisplayUrl(ctx, url)
+	if *stream {
+		parsed, err := url.Parse(urlStr)
+		if err != nil {
+			return err
+		}
+
+		var reader io.Reader
+		var mimeType string
+
+		if parsed.Scheme == "file" {
+			file, err := os.Open(parsed.Path)
+			if err != nil {
+				return err
+			}
+			defer file.Close()
+			reader = file
+			mimeType = mime.TypeByExtension(filepath.Ext(parsed.Path))
+		} else {
+			resp, err := http.Get(urlStr)
+			if err != nil {
+				return err
+			}
+			defer resp.Body.Close()
+			reader = resp.Body
+			mimeType = resp.Header.Get("Content-Type")
+		}
+
+		call, err := client.DisplayBytes(ctx, mimeType)
+		if err != nil {
+			return fmt.Errorf("Failed to start RPC: %v", err)
+		}
+		sw := &streamWriter{stream: call.SendStream()}
+		if _, err = io.Copy(sw, reader); err != nil {
+			return fmt.Errorf("Failed copy stream: %v", err)
+		}
+		return call.Finish()
+	} else {
+		return client.DisplayUrl(ctx, urlStr)
+	}
 }
diff --git a/go/src/v.io/x/media_sharing/mediaserver/mediaserver.go b/go/src/v.io/x/media_sharing/mediaserver/mediaserver.go
index 5488070..c242fb5 100644
--- a/go/src/v.io/x/media_sharing/mediaserver/mediaserver.go
+++ b/go/src/v.io/x/media_sharing/mediaserver/mediaserver.go
@@ -97,16 +97,19 @@
 		return nil, err
 	}
 	if _, err := io.Copy(tmp, r); err != nil {
+		os.Remove(tmp.Name())
 		return nil, err
 	}
+	tmp.Close()
 
 	cmd := exec.Command("eog", "--display", ":0", "-f", tmp.Name())
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
 	stop := func() {
-		if cmd.Process != nil {
-			if err := cmd.Process.Kill(); err != nil {
-				vlog.Errorf("Could not kill eog: %v", err)
-			}
+		if err := cmd.Process.Kill(); err != nil {
+			vlog.Errorf("Could not kill eog: %v", err)
 		}
+		cmd.Wait()
 		os.Remove(tmp.Name())
 	}
 	if err := cmd.Start(); err != nil {
@@ -115,6 +118,47 @@
 	return stop, nil
 }
 
+type omxHandler struct{}
+
+func (omxHandler) Matches(mimetype string) bool {
+	return strings.HasPrefix(mimetype, "video")
+}
+
+func (omxHandler) Display(ctx *context.T, mimetype string, r io.ReadCloser) (func(), error) {
+	defer r.Close()
+	tmp, err := ioutil.TempFile("", "")
+	if err != nil {
+		return nil, err
+	}
+	if _, err := io.Copy(tmp, r); err != nil {
+		os.Remove(tmp.Name())
+		return nil, err
+	}
+	tmp.Close()
+
+	args := []string{
+		"-b",
+		tmp.Name(),
+	}
+
+	vlog.Infof("Running: omxplayer %s", strings.Join(args, " "))
+
+	cmd := exec.Command("omxplayer", args...)
+	cmd.Stdin = r
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Start(); err != nil {
+		return nil, err
+	}
+	return func() {
+		if err := cmd.Process.Kill(); err != nil {
+			vlog.Errorf("Could not kill omx: %v", err)
+		}
+		cmd.Wait()
+		os.Remove(tmp.Name())
+	}, nil
+}
+
 type vlcHandler struct{}
 
 func (vlcHandler) Matches(mimetype string) bool {
@@ -122,11 +166,22 @@
 }
 
 func (vlcHandler) Display(ctx *context.T, mimetype string, r io.ReadCloser) (func(), error) {
+	defer r.Close()
+	tmp, err := ioutil.TempFile("", "")
+	if err != nil {
+		return nil, err
+	}
+	if _, err := io.Copy(tmp, r); err != nil {
+		os.Remove(tmp.Name())
+		return nil, err
+	}
+	tmp.Close()
+
 	args := []string{
 		"--no-video-title-show",
 		"--fullscreen",
 		"--x11-display", ":0",
-		"-",
+		tmp.Name(),
 		"vlc://quit",
 	}
 
@@ -134,16 +189,20 @@
 		args = append([]string{"--audio-visual=visual"}, args...)
 	}
 
+	vlog.Infof("Running: vlc %s", strings.Join(args, " "))
+
 	cmd := exec.Command("vlc", args...)
-	cmd.Stdin = r
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
 	if err := cmd.Start(); err != nil {
 		return nil, err
 	}
 	return func() {
-		r.Close()
 		if err := cmd.Process.Kill(); err != nil {
 			vlog.Errorf("Could not kill vlc: %v", err)
 		}
+		cmd.Wait()
+		os.Remove(tmp.Name())
 	}, nil
 }
 
@@ -158,6 +217,7 @@
 	m := &media{
 		handlers: []handler{
 			&eogHandler{},
+			&omxHandler{},
 			&vlcHandler{},
 		},
 	}
diff --git a/scripts/update.sh b/scripts/update.sh
index 8ca52c5..4d75470 100755
--- a/scripts/update.sh
+++ b/scripts/update.sh
@@ -11,6 +11,9 @@
 cp $V23_ROOT/release/projects/media-sharing/go/bin/linux_arm/* $V23_ROOT/release/go/bin/linux_arm
 vbecome --role=identity/role/vprod/publisher device publish --goos=linux --goarch=arm mediaserver
 
-installation=$(device ls --installation-state=Active vlab/devices/rpi2media/devmgr/apps/mediaserver/* | tail -n 1)
+#installation=$(device ls --installation-state=Active vlab/devices/rpi2media/devmgr/apps/mediaserver/* | tail -n 1)
+installation=$(namespace glob vlab/devices/rpi2media/devmgr/apps/mediaserver/* | tail -n 1)
+instance=$(namespace glob $installation/* | tail -n 1)
 
-device updateall $installation
+device update -parallelism=BYKIND $installation
+device update -parallelism=BYKIND $instance