18f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown/*
28f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * Copyright (C) 2013 The Android Open Source Project
38f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *
48f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
58f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * you may not use this file except in compliance with the License.
68f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * You may obtain a copy of the License at
78f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *
88f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
98f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown *
108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * Unless required by applicable law or agreed to in writing, software
118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * See the License for the specific language governing permissions and
148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown * limitations under the License.
158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown */
168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownpackage com.android.accessorydisplay.source;
188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport com.android.accessorydisplay.common.Protocol;
208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport com.android.accessorydisplay.common.Service;
218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport com.android.accessorydisplay.common.Transport;
228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.content.Context;
248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.display.DisplayManager;
258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.hardware.display.VirtualDisplay;
268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaCodec;
278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaCodec.BufferInfo;
288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaCodecInfo;
298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.media.MediaFormat;
308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.os.Handler;
318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.os.Message;
328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.Display;
338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport android.view.Surface;
348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
3583511d2f49c9e272f328730586c3d0a7852247f2Andy Hungimport java.io.IOException;
368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownimport java.nio.ByteBuffer;
378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brownpublic class DisplaySourceService extends Service {
398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int MSG_DISPATCH_DISPLAY_ADDED = 1;
408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int MSG_DISPATCH_DISPLAY_REMOVED = 2;
418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final String DISPLAY_NAME = "Accessory Display";
438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int BIT_RATE = 6000000;
448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int FRAME_RATE = 30;
458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private static final int I_FRAME_INTERVAL = 10;
468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final Callbacks mCallbacks;
488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final ServiceHandler mHandler;
498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final DisplayManager mDisplayManager;
508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private boolean mSinkAvailable;
528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private int mSinkWidth;
538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private int mSinkHeight;
548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private int mSinkDensityDpi;
558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private VirtualDisplayThread mVirtualDisplayThread;
578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public DisplaySourceService(Context context, Transport transport, Callbacks callbacks) {
598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super(context, transport, Protocol.DisplaySourceService.ID);
608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mCallbacks = callbacks;
618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mHandler = new ServiceHandler();
628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void start() {
678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super.start();
688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        getLogger().log("Sending MSG_QUERY.");
708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        getTransport().sendMessage(Protocol.DisplaySinkService.ID,
718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                Protocol.DisplaySinkService.MSG_QUERY, null);
728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void stop() {
768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        super.stop();
778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        handleSinkNotAvailable();
798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    @Override
828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public void onMessageReceived(int service, int what, ByteBuffer content) {
838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        switch (what) {
848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            case Protocol.DisplaySourceService.MSG_SINK_AVAILABLE: {
858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                getLogger().log("Received MSG_SINK_AVAILABLE");
868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (content.remaining() >= 12) {
878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int width = content.getInt();
888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int height = content.getInt();
898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    final int densityDpi = content.getInt();
908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (width >= 0 && width <= 4096
918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            && height >= 0 && height <= 4096
928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            && densityDpi >= 60 && densityDpi <= 640) {
938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        handleSinkAvailable(width, height, densityDpi);
948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        return;
958f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
968f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
978f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                getLogger().log("Receive invalid MSG_SINK_AVAILABLE message.");
988f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                break;
998f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1008f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1018f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            case Protocol.DisplaySourceService.MSG_SINK_NOT_AVAILABLE: {
1028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                getLogger().log("Received MSG_SINK_NOT_AVAILABLE");
1038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                handleSinkNotAvailable();
1048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                break;
1058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1068f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1078f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void handleSinkAvailable(int width, int height, int densityDpi) {
1108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mSinkAvailable && mSinkWidth == width && mSinkHeight == height
1118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                && mSinkDensityDpi == densityDpi) {
1128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            return;
1138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        getLogger().log("Accessory display sink available: "
1168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                + "width=" + width + ", height=" + height
1178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                + ", densityDpi=" + densityDpi);
1188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkAvailable = true;
1198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkWidth = width;
1208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkHeight = height;
1218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkDensityDpi = densityDpi;
1228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        createVirtualDisplay();
1238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void handleSinkNotAvailable() {
1268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        getLogger().log("Accessory display sink not available.");
1278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkAvailable = false;
1298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkWidth = 0;
1308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkHeight = 0;
1318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mSinkDensityDpi = 0;
1328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        releaseVirtualDisplay();
1338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void createVirtualDisplay() {
1368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        releaseVirtualDisplay();
1378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mVirtualDisplayThread = new VirtualDisplayThread(
1398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mSinkWidth, mSinkHeight, mSinkDensityDpi);
1408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        mVirtualDisplayThread.start();
1418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private void releaseVirtualDisplay() {
1448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        if (mVirtualDisplayThread != null) {
1458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mVirtualDisplayThread.quit();
1468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mVirtualDisplayThread = null;
1478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    public interface Callbacks {
1518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void onDisplayAdded(Display display);
1528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void onDisplayRemoved(Display display);
1538f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1548f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1558f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final class ServiceHandler extends Handler {
1568f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        @Override
1578f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void handleMessage(Message msg) {
1588f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            switch (msg.what) {
1598f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                case MSG_DISPATCH_DISPLAY_ADDED: {
1608f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mCallbacks.onDisplayAdded((Display)msg.obj);
1618f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    break;
1628f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
1638f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1648f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                case MSG_DISPATCH_DISPLAY_REMOVED: {
1658f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    mCallbacks.onDisplayRemoved((Display)msg.obj);
1668f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    break;
1678f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
1688f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
1698f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1708f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
1718f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1728f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    private final class VirtualDisplayThread extends Thread {
1738f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private static final int TIMEOUT_USEC = 1000000;
1748f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1758f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private final int mWidth;
1768f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private final int mHeight;
1778f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private final int mDensityDpi;
1788f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1798f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private volatile boolean mQuitting;
1808f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1818f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public VirtualDisplayThread(int width, int height, int densityDpi) {
1828f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mWidth = width;
1838f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mHeight = height;
1848f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mDensityDpi = densityDpi;
1858f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
1868f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
1878f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        @Override
1888f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void run() {
1898f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
1908f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
1918f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
1928f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
1938f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
1948f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
19583511d2f49c9e272f328730586c3d0a7852247f2Andy Hung            MediaCodec codec;
19683511d2f49c9e272f328730586c3d0a7852247f2Andy Hung            try {
19783511d2f49c9e272f328730586c3d0a7852247f2Andy Hung                codec = MediaCodec.createEncoderByType("video/avc");
19883511d2f49c9e272f328730586c3d0a7852247f2Andy Hung            } catch (IOException e) {
19983511d2f49c9e272f328730586c3d0a7852247f2Andy Hung                throw new RuntimeException(
20083511d2f49c9e272f328730586c3d0a7852247f2Andy Hung                        "failed to create video/avc encoder", e);
20183511d2f49c9e272f328730586c3d0a7852247f2Andy Hung            }
2028f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
2038f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            Surface surface = codec.createInputSurface();
2048f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            codec.start();
2058f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2067d00affce6e25b22fd8fc135933b3bf6b547a0dcJeff Brown            VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(
2077d00affce6e25b22fd8fc135933b3bf6b547a0dcJeff Brown                    DISPLAY_NAME, mWidth, mHeight, mDensityDpi, surface, 0);
2088f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            if (virtualDisplay != null) {
2098f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mHandler.obtainMessage(MSG_DISPATCH_DISPLAY_ADDED,
2108f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        virtualDisplay.getDisplay()).sendToTarget();
2118f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2128f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                stream(codec);
2138f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2148f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                mHandler.obtainMessage(MSG_DISPATCH_DISPLAY_REMOVED,
2158f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        virtualDisplay.getDisplay()).sendToTarget();
2168f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                virtualDisplay.release();
2178f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2188f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2198f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            codec.signalEndOfInputStream();
2208f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            codec.stop();
2218f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2228f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2238f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        public void quit() {
2248f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            mQuitting = true;
2258f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2268f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2278f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        private void stream(MediaCodec codec) {
2288f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            BufferInfo info = new BufferInfo();
2298f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            ByteBuffer[] buffers = null;
2308f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            while (!mQuitting) {
2318f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                int index = codec.dequeueOutputBuffer(info, TIMEOUT_USEC);
2328f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                if (index >= 0) {
2338f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    if (buffers == null) {
2348f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                        buffers = codec.getOutputBuffers();
2358f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    }
2368f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2378f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    ByteBuffer buffer = buffers[index];
2388f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer.limit(info.offset + info.size);
2398f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffer.position(info.offset);
2408f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown
2418f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    getTransport().sendMessage(Protocol.DisplaySinkService.ID,
2428f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                            Protocol.DisplaySinkService.MSG_CONTENT, buffer);
2438f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    codec.releaseOutputBuffer(index, false);
2448f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
2458f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    buffers = null;
2468f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                } else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
2478f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                    getLogger().log("Codec dequeue buffer timed out.");
2488f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown                }
2498f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown            }
2508f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown        }
2518f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown    }
2528f3b1307678fcd1896c7fb8ba4cc20553dc032e8Jeff Brown}
253