我正在寻找一个在Android中将图像投射到Chromecast的示例。奇怪的是,谷歌的示例存储库似乎没有涵盖这个问题。有没有人有一个简单的实现?我想在我的应用程序相册中点击一张图片,并将其投射到屏幕上。
另外一个问题是,这个图像需要在URL上吗?还是可以将图像流式传输到设备上?提前感谢您的帮助。
我正在寻找一个在Android中将图像投射到Chromecast的示例。奇怪的是,谷歌的示例存储库似乎没有涵盖这个问题。有没有人有一个简单的实现?我想在我的应用程序相册中点击一张图片,并将其投射到屏幕上。
另外一个问题是,这个图像需要在URL上吗?还是可以将图像流式传输到设备上?提前感谢您的帮助。
这是接收器的代码:
<!DOCTYPE html>
<html>
<head>
<style>
img#androidImage {
height:auto;
width:100%;
}
</style>
<title>Cast Hello Text</title>
</head>
<body>
<img id="androidImage" src="" />
<script type="text/javascript" src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
<script type="text/javascript">
window.onload = function() {
cast.receiver.logger.setLevelValue(0);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
console.log('Starting Receiver Manager');
// handler for the 'ready' event
castReceiverManager.onReady = function(event) {
console.log('Received Ready event: ' + JSON.stringify(event.data));
window.castReceiverManager.setApplicationState("Application status is ready...");
};
// handler for 'senderconnected' event
castReceiverManager.onSenderConnected = function(event) {
console.log('Received Sender Connected event: ' + event.data);
console.log(window.castReceiverManager.getSender(event.data).userAgent);
};
// handler for 'senderdisconnected' event
castReceiverManager.onSenderDisconnected = function(event) {
console.log('Received Sender Disconnected event: ' + event.data);
if (window.castReceiverManager.getSenders().length == 0) {
window.close();
}
};
// handler for 'systemvolumechanged' event
castReceiverManager.onSystemVolumeChanged = function(event) {
console.log('Received System Volume Changed event: ' + event.data['level'] + ' ' +
event.data['muted']);
};
// create a CastMessageBus to handle messages for a custom namespace
window.messageBus =
window.castReceiverManager.getCastMessageBus(
'urn:x-cast:com.google.cast.sample.helloworld');
// handler for the CastMessageBus message event
window.messageBus.onMessage = function(event) {
console.log('Message recieved');
var obj = JSON.parse(event.data)
console.log('Message type: ' + obj.type);
if (obj.type == "text") {
console.log('Skipping message: ' + obj.data);
}
if (obj.type == "image") {
var source = 'data:image/png;base64,'.concat(obj.data)
displayImage(source);
}
// inform all senders on the CastMessageBus of the incoming message event
// sender message listener will be invoked
window.messageBus.send(event.senderId, event.data);
}
// initialize the CastReceiverManager with an application status message
window.castReceiverManager.start({statusText: "Application is starting"});
console.log('Receiver Manager started');
};
function displayImage(source) {
console.log('received image');
document.getElementById("androidImage").src=source;
window.castReceiverManager.setApplicationState('image source changed');
};
</script>
</body>
</html>
MainActivity.java:
package com.example.casthelloworld;
import java.io.IOException;
import java.util.ArrayList;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.Cast.ApplicationConnectionResult;
import com.google.android.gms.cast.Cast.MessageReceivedCallback;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
/**
* Main activity to send messages to the receiver.
*/
public class MainActivity extends ActionBarActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int REQUEST_CODE = 1;
private MediaRouter mMediaRouter;
private MediaRouteSelector mMediaRouteSelector;
private MediaRouter.Callback mMediaRouterCallback;
private CastDevice mSelectedDevice;
private GoogleApiClient mApiClient;
private Cast.Listener mCastListener;
private ConnectionCallbacks mConnectionCallbacks;
private ConnectionFailedListener mConnectionFailedListener;
private HelloWorldChannel mHelloWorldChannel;
private boolean mApplicationStarted;
private boolean mWaitingForReconnect;
private String mSessionId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(new ColorDrawable(
android.R.color.transparent));
// When the user clicks on the button, use Android voice recognition to
// get text
Button voiceButton = (Button) findViewById(R.id.voiceButton);
voiceButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startVoiceRecognitionActivity();
}
});
// When the user clicks on the button, use Android voice recognition to
// get text
Button yarrButton = (Button) findViewById(R.id.tmpButton);
yarrButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
castImage();
}
});
// Configure Cast device discovery
mMediaRouter = MediaRouter.getInstance(getApplicationContext());
mMediaRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory(
CastMediaControlIntent.categoryForCast(getResources()
.getString(R.string.app_id))).build();
mMediaRouterCallback = new MyMediaRouterCallback();
}
private void castImage()
{
Log.d(TAG, "castImage()");
String image_string = createJsonMessage(MessageType.image, ENCODED_IMAGE_STRING);
sendMessage(image_string);
}
/**
* Android voice recognition
*/
private void startVoiceRecognitionActivity() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
getString(R.string.message_to_cast));
startActivityForResult(intent, REQUEST_CODE);
}
/*
* Handle the voice recognition response
*
* @see android.support.v4.app.FragmentActivity#onActivityResult(int, int,
* android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
ArrayList<String> matches = data
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
if (matches.size() > 0) {
Log.d(TAG, matches.get(0));
String message = createJsonMessage(MessageType.text, matches.get(0));
sendMessage(message);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onResume() {
super.onResume();
// Start media router discovery
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
@Override
protected void onPause() {
if (isFinishing()) {
// End media router discovery
mMediaRouter.removeCallback(mMediaRouterCallback);
}
super.onPause();
}
@Override
public void onDestroy() {
teardown();
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.main, menu);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat
.getActionProvider(mediaRouteMenuItem);
// Set the MediaRouteActionProvider selector for device discovery.
mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
return true;
}
/**
* Callback for MediaRouter events
*/
private class MyMediaRouterCallback extends MediaRouter.Callback {
@Override
public void onRouteSelected(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRouteSelected");
// Handle the user route selection.
mSelectedDevice = CastDevice.getFromBundle(info.getExtras());
launchReceiver();
}
@Override
public void onRouteUnselected(MediaRouter router, RouteInfo info) {
Log.d(TAG, "onRouteUnselected: info=" + info);
teardown();
mSelectedDevice = null;
}
}
/**
* Start the receiver app
*/
private void launchReceiver() {
try {
mCastListener = new Cast.Listener() {
@Override
public void onApplicationDisconnected(int errorCode) {
Log.d(TAG, "application has stopped");
teardown();
}
};
// Connect to Google Play services
mConnectionCallbacks = new ConnectionCallbacks();
mConnectionFailedListener = new ConnectionFailedListener();
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder(mSelectedDevice, mCastListener);
mApiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptionsBuilder.build())
.addConnectionCallbacks(mConnectionCallbacks)
.addOnConnectionFailedListener(mConnectionFailedListener)
.build();
mApiClient.connect();
} catch (Exception e) {
Log.e(TAG, "Failed launchReceiver", e);
}
}
/**
* Google Play services callbacks
*/
private class ConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks {
@Override
public void onConnected(Bundle connectionHint) {
Log.d(TAG, "onConnected");
if (mApiClient == null) {
// We got disconnected while this runnable was pending
// execution.
return;
}
try {
if (mWaitingForReconnect) {
mWaitingForReconnect = false;
// Check if the receiver app is still running
if ((connectionHint != null)
&& connectionHint
.getBoolean(Cast.EXTRA_APP_NO_LONGER_RUNNING)) {
Log.d(TAG, "App is no longer running");
teardown();
} else {
// Re-create the custom message channel
try {
Cast.CastApi.setMessageReceivedCallbacks(
mApiClient,
mHelloWorldChannel.getNamespace(),
mHelloWorldChannel);
} catch (IOException e) {
Log.e(TAG, "Exception while creating channel", e);
}
}
} else {
// Launch the receiver app
Cast.CastApi
.launchApplication(mApiClient,
getString(R.string.app_id), false)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
@Override
public void onResult(
ApplicationConnectionResult result) {
Status status = result.getStatus();
Log.d(TAG,
"ApplicationConnectionResultCallback.onResult: statusCode "
+ status.getStatusCode());
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result
.getApplicationMetadata();
mSessionId = result
.getSessionId();
String applicationStatus = result
.getApplicationStatus();
boolean wasLaunched = result
.getWasLaunched();
Log.d(TAG,
"application name: "
+ applicationMetadata
.getName()
+ ", status: "
+ applicationStatus
+ ", sessionId: "
+ mSessionId
+ ", wasLaunched: "
+ wasLaunched);
mApplicationStarted = true;
// Create the custom message
// channel
mHelloWorldChannel = new HelloWorldChannel();
try {
Cast.CastApi
.setMessageReceivedCallbacks(
mApiClient,
mHelloWorldChannel
.getNamespace(),
mHelloWorldChannel);
} catch (IOException e) {
Log.e(TAG,
"Exception while creating channel",
e);
}
// set the initial instructions
// on the receiver
String message = createJsonMessage(MessageType.text, getString(R.string.instructions));
sendMessage(message);
} else {
Log.e(TAG,
"application could not launch");
teardown();
}
}
});
}
} catch (Exception e) {
Log.e(TAG, "Failed to launch application", e);
}
}
@Override
public void onConnectionSuspended(int cause) {
Log.d(TAG, "onConnectionSuspended");
mWaitingForReconnect = true;
}
}
/**
* Google Play services callbacks
*/
private class ConnectionFailedListener implements
GoogleApiClient.OnConnectionFailedListener {
@Override
public void onConnectionFailed(ConnectionResult result) {
Log.e(TAG, "onConnectionFailed ");
teardown();
}
}
/**
* Tear down the connection to the receiver
*/
private void teardown() {
Log.d(TAG, "teardown");
if (mApiClient != null) {
if (mApplicationStarted) {
if (mApiClient.isConnected() || mApiClient.isConnecting()) {
try {
Cast.CastApi.stopApplication(mApiClient, mSessionId);
if (mHelloWorldChannel != null) {
Cast.CastApi.removeMessageReceivedCallbacks(
mApiClient,
mHelloWorldChannel.getNamespace());
mHelloWorldChannel = null;
}
} catch (IOException e) {
Log.e(TAG, "Exception while removing channel", e);
}
mApiClient.disconnect();
}
mApplicationStarted = false;
}
mApiClient = null;
}
mSelectedDevice = null;
mWaitingForReconnect = false;
mSessionId = null;
}
/**
* Send a text message to the receiver
*
* @param message
*/
private void sendMessage(String message) {
if (mApiClient != null && mHelloWorldChannel != null) {
try {
Cast.CastApi.sendMessage(mApiClient,
mHelloWorldChannel.getNamespace(), message)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status result) {
if (!result.isSuccess()) {
Log.e(TAG, "Sending message failed");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Exception while sending message", e);
}
} else {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT)
.show();
}
}
/**
* Custom message channel
*/
class HelloWorldChannel implements MessageReceivedCallback {
/**
* @return custom namespace
*/
public String getNamespace() {
return getString(R.string.namespace);
}
/*
* Receive message from the receiver app
*/
@Override
public void onMessageReceived(CastDevice castDevice, String namespace,
String message) {
Log.d(TAG, "onMessageReceived: " + message);
}
}
enum MessageType {
text,
image,
}
public static Bitmap getBitmapFromView(View view) {
//Define a bitmap with the same size as the view
Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
//Bind a canvas to it
Canvas canvas = new Canvas(returnedBitmap);
//Get the view's background
Drawable bgDrawable =view.getBackground();
if (bgDrawable!=null)
//has background drawable, then draw it on the canvas
bgDrawable.draw(canvas);
else
//does not have background drawable, then draw white background on the canvas
canvas.drawColor(Color.WHITE);
// draw the view on the canvas
view.draw(canvas);
//return the bitmap
return returnedBitmap;
}
private static String createJsonMessage(MessageType type, String message)
{
return String.format("{\"type\":\"%s\", \"data\":\"%s\"}", type.toString(), message);
}
}
<img/>
标记来显示图像。该标记的 src
属性应指向您想要查看的图像,并且必须是一个 URL,因此,如果您的图像存储在手机的本地存储中,则需要在移动应用程序中启动一个小型 Web 服务器以提供该图像并与接收器进行通信,告诉它指向哪个 URL(即您的服务器正在提供该图像的 URL)。所有这些都是可行的,并且您可以使用 CastCompanionLibrary(如果需要)与您的自定义接收器进行通信;只需使用 DataCastManager
类,而不是 VideoCastManager
。也许我的答案对其他开发者有帮助,因为我自己也没有找到好的解决方案,只能自己动手。
如果你想通过Google Cast在你的应用程序中显示图像并在设备屏幕上展示,你可以从你的应用程序创建并启动一个简单的Web服务器,该服务器将处理URL中选择的图像名称或ID的http请求。
例如:
public class MyWebServer {
private Activity activity;
private static ServerSocket httpServerSocket;
private static boolean isWebServerSunning;
public static final String drawableDelimiter = "pic-"
public MyWebServer(Activity activity) {
this.activity = activity;
}
public void stopWebServer() {
isWebServerSunning = false;
try {
if (httpServerSocket != null) {
httpServerSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void startWebServer() {
isWebServerSunning = true;
Thread webServerThread = new Thread(() -> {
Socket socket;
HttpResponseThread httpResponseThread;
try {
httpServerSocket = new ServerSocket(5050);
while (isWebServerSunning) {
socket = httpServerSocket.accept();
httpResponseThread = new HttpResponseThread(socket);
httpResponseThread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
});
webServerThread.start();
}
private class HttpResponseThread extends Thread {
Socket clientSocket;
HttpResponseThread(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
OutputStream outputStream = clientSocket.getOutputStream();
) {
String input = bufferedReader.readLine();
if (input != null && !input.isEmpty() && input.contains("/") && input.contains(" ")) {
if (input.contains(drawableDelimiter)) {
String imageId = input.substring(input.indexOf("/") + 1, input.lastIndexOf(" ")).trim().split(drawableDelimiter)[1];
Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), Integer.parseInt(imageId));
if (bitmap != null) {
ByteArrayOutputStream bitmapBytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bitmapBytes);
outputStream.write("HTTP/1.0 200 OK\r\n".getBytes());
outputStream.write("Server: Apache/0.8.4\r\n".getBytes());
outputStream.write(("Content-Length: " + bitmapBytes.toByteArray().length + "\r\n").getBytes());
outputStream.write("\r\n".getBytes());
outputStream.write(bitmapBytes.toByteArray());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
只需在Google Cast可用或停止时启动或停止您的Web服务器。
MyWebServer myWebServer = new MyWebServer(this); // pass your activity here
myWebServer.startWebServer();
myWebServer.stopWebServer();