1 /* 2 * Copyright 2012, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "wfd" 19 #include <utils/Log.h> 20 21 #include "source/WifiDisplaySource.h" 22 23 #include <binder/ProcessState.h> 24 #include <binder/IServiceManager.h> 25 #include <gui/ISurfaceComposer.h> 26 #include <gui/SurfaceComposerClient.h> 27 #include <media/AudioSystem.h> 28 #include <media/IMediaPlayerService.h> 29 #include <media/IRemoteDisplay.h> 30 #include <media/IRemoteDisplayClient.h> 31 #include <media/stagefright/DataSource.h> 32 #include <media/stagefright/foundation/ADebug.h> 33 #include <media/stagefright/foundation/AMessage.h> 34 #include <ui/DisplayInfo.h> 35 36 namespace android { 37 38 static void usage(const char *me) { 39 fprintf(stderr, 40 "usage:\n" 41 " %s -l iface[:port]\tcreate a wifi display source\n" 42 " -f(ilename) \tstream media\n", 43 me); 44 } 45 46 struct RemoteDisplayClient : public BnRemoteDisplayClient { 47 RemoteDisplayClient(); 48 49 virtual void onDisplayConnected( 50 const sp<IGraphicBufferProducer> &bufferProducer, 51 uint32_t width, 52 uint32_t height, 53 uint32_t flags); 54 55 virtual void onDisplayDisconnected(); 56 virtual void onDisplayError(int32_t error); 57 58 void waitUntilDone(); 59 60 protected: 61 virtual ~RemoteDisplayClient(); 62 63 private: 64 Mutex mLock; 65 Condition mCondition; 66 67 bool mDone; 68 69 sp<SurfaceComposerClient> mComposerClient; 70 sp<IGraphicBufferProducer> mSurfaceTexture; 71 sp<IBinder> mDisplayBinder; 72 73 DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient); 74 }; 75 76 RemoteDisplayClient::RemoteDisplayClient() 77 : mDone(false) { 78 mComposerClient = new SurfaceComposerClient; 79 CHECK_EQ(mComposerClient->initCheck(), (status_t)OK); 80 } 81 82 RemoteDisplayClient::~RemoteDisplayClient() { 83 } 84 85 void RemoteDisplayClient::onDisplayConnected( 86 const sp<IGraphicBufferProducer> &bufferProducer, 87 uint32_t width, 88 uint32_t height, 89 uint32_t flags) { 90 ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", 91 width, height, flags); 92 93 if (bufferProducer != NULL) { 94 mSurfaceTexture = bufferProducer; 95 mDisplayBinder = mComposerClient->createDisplay( 96 String8("foo"), false /* secure */); 97 98 SurfaceComposerClient::openGlobalTransaction(); 99 mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); 100 101 Rect layerStackRect(1280, 720); // XXX fix this. 102 Rect displayRect(1280, 720); 103 104 mComposerClient->setDisplayProjection( 105 mDisplayBinder, 0 /* 0 degree rotation */, 106 layerStackRect, 107 displayRect); 108 109 SurfaceComposerClient::closeGlobalTransaction(); 110 } 111 } 112 113 void RemoteDisplayClient::onDisplayDisconnected() { 114 ALOGI("onDisplayDisconnected"); 115 116 Mutex::Autolock autoLock(mLock); 117 mDone = true; 118 mCondition.broadcast(); 119 } 120 121 void RemoteDisplayClient::onDisplayError(int32_t error) { 122 ALOGI("onDisplayError error=%d", error); 123 124 Mutex::Autolock autoLock(mLock); 125 mDone = true; 126 mCondition.broadcast(); 127 } 128 129 void RemoteDisplayClient::waitUntilDone() { 130 Mutex::Autolock autoLock(mLock); 131 while (!mDone) { 132 mCondition.wait(mLock); 133 } 134 } 135 136 static status_t enableAudioSubmix(bool enable) { 137 status_t err = AudioSystem::setDeviceConnectionState( 138 AUDIO_DEVICE_IN_REMOTE_SUBMIX, 139 enable 140 ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE 141 : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, 142 NULL /* device_address */); 143 144 if (err != OK) { 145 return err; 146 } 147 148 err = AudioSystem::setDeviceConnectionState( 149 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, 150 enable 151 ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE 152 : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, 153 NULL /* device_address */); 154 155 return err; 156 } 157 158 static void createSource(const AString &addr, int32_t port) { 159 sp<IServiceManager> sm = defaultServiceManager(); 160 sp<IBinder> binder = sm->getService(String16("media.player")); 161 sp<IMediaPlayerService> service = 162 interface_cast<IMediaPlayerService>(binder); 163 164 CHECK(service.get() != NULL); 165 166 enableAudioSubmix(true /* enable */); 167 168 String8 iface; 169 iface.append(addr.c_str()); 170 iface.append(StringPrintf(":%d", port).c_str()); 171 172 sp<RemoteDisplayClient> client = new RemoteDisplayClient; 173 sp<IRemoteDisplay> display = service->listenForRemoteDisplay(client, iface); 174 175 client->waitUntilDone(); 176 177 display->dispose(); 178 display.clear(); 179 180 enableAudioSubmix(false /* enable */); 181 } 182 183 static void createFileSource( 184 const AString &addr, int32_t port, const char *path) { 185 sp<ANetworkSession> session = new ANetworkSession; 186 session->start(); 187 188 sp<ALooper> looper = new ALooper; 189 looper->start(); 190 191 sp<RemoteDisplayClient> client = new RemoteDisplayClient; 192 sp<WifiDisplaySource> source = new WifiDisplaySource(session, client, path); 193 looper->registerHandler(source); 194 195 AString iface = StringPrintf("%s:%d", addr.c_str(), port); 196 CHECK_EQ((status_t)OK, source->start(iface.c_str())); 197 198 client->waitUntilDone(); 199 200 source->stop(); 201 } 202 203 } // namespace android 204 205 int main(int argc, char **argv) { 206 using namespace android; 207 208 ProcessState::self()->startThreadPool(); 209 210 DataSource::RegisterDefaultSniffers(); 211 212 AString listenOnAddr; 213 int32_t listenOnPort = -1; 214 215 AString path; 216 217 int res; 218 while ((res = getopt(argc, argv, "hl:f:")) >= 0) { 219 switch (res) { 220 case 'f': 221 { 222 path = optarg; 223 break; 224 } 225 226 case 'l': 227 { 228 const char *colonPos = strrchr(optarg, ':'); 229 230 if (colonPos == NULL) { 231 listenOnAddr = optarg; 232 listenOnPort = WifiDisplaySource::kWifiDisplayDefaultPort; 233 } else { 234 listenOnAddr.setTo(optarg, colonPos - optarg); 235 236 char *end; 237 listenOnPort = strtol(colonPos + 1, &end, 10); 238 239 if (*end != '\0' || end == colonPos + 1 240 || listenOnPort < 1 || listenOnPort > 65535) { 241 fprintf(stderr, "Illegal port specified.\n"); 242 exit(1); 243 } 244 } 245 break; 246 } 247 248 case '?': 249 case 'h': 250 default: 251 usage(argv[0]); 252 exit(1); 253 } 254 } 255 256 if (listenOnPort >= 0) { 257 if (path.empty()) { 258 createSource(listenOnAddr, listenOnPort); 259 } else { 260 createFileSource(listenOnAddr, listenOnPort, path.c_str()); 261 } 262 263 exit(0); 264 } 265 266 usage(argv[0]); 267 268 return 0; 269 } 270