Am working on a webrtc android application and everything is working fine except two things...
which are;
switching the default sound output device from the Earpiece to Speaker and vice versa..
I have tried to use the code below from this stackoverflow thread but they are not working
audioManager = (AudioManager) this.activity.getSystemService(Context.AUDIO_SERVICE);
audioManager.setSpeakerphoneOn(true);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
And improving the video performance, there is a lot of lagging in the video streams where even the stream hangs a lot. if there is any one who can help me on this too thanks a lot.
Below is my peerConnection configs
String fieldTrials = (PeerConnectionFactory.VIDEO_FRAME_EMIT_TRIAL + "/" + PeerConnectionFactory.TRIAL_ENABLED + "/");
PeerConnectionFactory.InitializationOptions initializationOptions =
PeerConnectionFactory.InitializationOptions.builder(this)
.setFieldTrials(fieldTrials)
.createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);
//Create a new PeerConnectionFactory instance - using Hardware encoder and decoder.
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(
rootEglBase.getEglBaseContext(), /* enableIntelVp8Encoder */true, /* enableH264HighProfile */true);
DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
peerConnectionFactory = PeerConnectionFactory.builder()
.setOptions(options)
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.createPeerConnectionFactory();
Thanks
Need add this permission in manifest
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
Related
I am using exoplayer in recyclerview to render a list of videos. This recyclerview is rendered in an activity and I know the list of video urls before opening the activity.
I want to be able to preload or cache the videos before going to that activity. The videos are usually less than 1 minute. So I am not looking for a solution to smooth stream videos. I just want the videos to be in the cache before opening the activity so that once the recyclerview opens the videos start playing without any buffering just like in tiktok.
I found a way to cache already played videos using LocalCacheDataSourceFactory in
MediaSource videoSource =
new ExtractorMediaSource(Uri.parse(mediaUrl),
new LocalCacheDataSourceFactory(context, 100 * 1024 * 1024, 5 * 1024 * 1024), new DefaultExtractorsFactory(), null, null);
This only allows me to cache the videos that are already played but not preloading or precaching them.
Found this medium article from exoplayer team but no other example integration for my specific requirement. article
Checkout this article. I have written a step by step guide to preload/precache videos using Exoplayer2.
In your case you must create a recursion or looping of caching method inside a background thread.
Video preloading/ precaching using Exoplayer 2 in Android
I have achieved this in my own app. Follow these steps:
add this video caching dependency:
implementation 'com.danikula:videocache:2.7.1'
Setup dependency like this:
It is advised that you do this in the application class:
private HttpProxyCacheServer proxy;
public static HttpProxyCacheServer getProxy(Context context) {
AppController app = (AppController) context.getApplicationContext();
return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;
}
private HttpProxyCacheServer newProxy() {
return new HttpProxyCacheServer(this);
}
//Note that you need to have a single instance of HttpProxyCacheServer.
//Note the library is tradinally meant to allow you to cache a video so exoplayer does not re-buffer it every single time you scroll to recycler item.
Then setup your exoplayer like this:
a.
HttpProxyCacheServer proxyServer; // maintain a single instance of this. you can init this in oncreate method:
public ViewRecommendedShortVideosAdapter(Context context) {
proxyServer = AppController.getProxy(context);
}
b. In your method to play exoplayer file:
String streamingURL = shortVideo.getStreamingURL();
String proxyURL = proxyServer.getProxyUrl(streamingURL);
MediaItem mediaItem = MediaItem.fromUri(proxyURL); // you are now crating the media item from the proxy url you get by calling getProxyUrl on your proxyServer instance.
exoPlayer.setMediaItem(mediaItem);
exoPlayer.prepare();
exoPlayer.setPlayWhenReady(true);
Note: the above still only gives you caching of the video once it has been played, and not caching of yet to play videos.
To cache yet to play videos, do this:
a. you can cache only the next video in the recycler data arraylist (this helps save user internet costs) or you can cache the whole (I don't recommend, cos of data costs on users).
b. To cache the next video, create a method such as prepareNextVideo(), this will contain the logic to cache the next video. you will call this method in the original method to play an exoplayer media item. (maybe after setting up the first media item and set exoplayer.play()).
c. the code in the prepareNextVideo():
private void prepareNextVideo(int currentPosition){
URL url = null;
try {
ShortVideo shortVideo = videosArrayList.get(currentPosition+1); //you can put some if conditions here to check if the element exists. but a try cache should handle it to.
url = new URL(proxyServer.getProxyUrl(shortVideo.getStreamingURL())); // we are using our proxy url again.
InputStream inputStream = url.openStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int length = 0;
while ((length = inputStream.read(buffer)) != -1) {
//nothing to do here. you are just reading the stream.
}
} catch (Exception e) {
e.printStackTrace();
//can fetch
}
}
Finally, simply call this method to buffer the next video. you can also put it in a loop, run asynchronously if you want to buffer all the videos.
I have created an application which transfer data from android to android over wifi. I am exploring usb host apis to add support for data transfer over usb. I am following android docs for host apis
https://developer.android.com/guide/topics/connectivity/usb/host
and followed all the steps. So far I am able to grant permission to the selected device lets say phone "A" and opened usb device successfully to write data , but i don't know how to create/access usbdevice on other phone "B"? I have seen other phone isn't notified about the usb attached/unattached events. When I added "android.hardware.usb.action.USB_STATE" other side is notified by event change but I don't know how to get/access usbdevice object on phone "B" to initialize read/write data? Does the current host protocol is one sided protocol? Any help in this regard will be highly appreciated.
After reading further I was able to identify the problem. I had to force accessory mode in device from host, by sending special controlTransfer messages.
I found it here
https://github.com/peyo-hd/TcpDisplay/blob/master/sink/src/com/android/accessorydisplay/sink/SinkActivity.java
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MANUFACTURER, MANUFACTURER);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_MODEL, MODEL);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_DESCRIPTION, DESCRIPTION);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_VERSION, VERSION);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_URI, URI);
sendString(conn, UsbAccessoryConstants.ACCESSORY_STRING_SERIAL, SERIAL);
// The device should re-enumerate as an accessory.
conn.controlTransfer(UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
UsbAccessoryConstants.ACCESSORY_START, 0, 0, null, 0, 10000);
Phone B is UsbDevice, it should use the USB Accessory function to connect with Accessory host(Phone A).
Phone B selects to accessory usb gadget function, which means Phone B's sys.usb.config = accessory. [getprop sys.usb.config => accessory]
App level design refer to AOA:
https://source.android.com/devices/accessories/protocol
https://developer.android.com/guide/topics/connectivity/usb/accessory.html#java
UsbAccessory accessory;
ParcelFileDescriptor fileDescriptor;
FileInputStream inputStream;
FileOutputStream outputStream;
...
private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
fileDescriptor = usbManager.openAccessory(accessory);
if (fileDescriptor != null) {
FileDescriptor fd = fileDescriptor.getFileDescriptor();
inputStream = new FileInputStream(fd);
outputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}
This question is related to this previously answered question Share Image on Android Application from Unity Game
My question is this:
Is there a way to share the image WITHOUT using a file? I'd like to remove the sd card read/write permission on Android. I'm not a Java programmer, so I'm having a heck of a time getting it working. None of my attempts have yielded anything.
"Programmer" answered the previous question with the below code, which works great for me. I basically want to use the variable imageBytes instead of the file. Is this even possible?
void takeScreenShotAndShare()
{
StartCoroutine(takeScreenshotAndSave());
}
private IEnumerator takeScreenshotAndSave()
{
string path = "";
yield return new WaitForEndOfFrame();
Texture2D screenImage = new Texture2D(Screen.width, Screen.height);
//Get Image from screen
screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
screenImage.Apply();
//Convert to png
byte[] imageBytes = screenImage.EncodeToPNG();
System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/GameOverScreenShot");
path = Application.persistentDataPath + "/GameOverScreenShot" + "/DiedScreenShot.png";
System.IO.File.WriteAllBytes(path, imageBytes);
StartCoroutine(shareScreenshot(path));
}
private IEnumerator shareScreenshot(string destination)
{
string ShareSubject = "Picture Share";
string shareLink = "Test Link" + "\nhttps://stackoverflow.com/questions/36512784/share-image-on-android-application-from-unity-game";
string textToShare = "Text To share";
Debug.Log(destination);
if (!Application.isEditor)
{
AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent");
AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent");
intentObject.Call<AndroidJavaObject>("setAction", intentClass.GetStatic<string>("ACTION_SEND"));
AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
AndroidJavaObject uriObject = uriClass.CallStatic<AndroidJavaObject>("parse", "file://" + destination);
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_STREAM"), uriObject);
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TEXT"), textToShare + shareLink);
intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_SUBJECT"), ShareSubject);
intentObject.Call<AndroidJavaObject>("setType", "image/png");
AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
currentActivity.Call("startActivity", intentObject);
}
yield return null;
}
Unity3d share image on android
You can directly pass the byte array, no need to convert it to file -> uri -> string
Below are java code, but you can easily translate this to JNI wrapper calls
Bundle bundle = new Bundle();
bundle.putByteArray("byteArr", imageBytes); //put byte array to bundle!!!
Intent intent = new Intent(...);
intent.putExtra(bundle );
To retrieve the image byte array in another activity, use below code:
getIntent().getByteArrayExtra("byteArr");
If you wrap each above function calls into a corresponding JNI function, it would be very tedious. You can on the other hand:
Create an android library project
Add two static java functions, one is saveByteArrayToBundle(...), and the other is getByteArrayFromBundle(...)
Compile the android library into an aar file;
then put the aar file into the Assets/ folder (actually any subfolders will do)
Then call the two static java functions as you have done in the OP
Refer to below tutorials if you need to go through step-by-step:
Step-by-Step guide for developing Android Plugin for Unity3D (I)
Step-by-Step guide for developing Android Plugin for Unity3D (II)
I'm building a relay service that will pass videos from an external server to YouTube. Currently my code is working as intended but I need to avoid to save the file locally ahead of time to do the upload to YouTube using MediaFileSource. Is there a way to pass an InputStream instead of a file and use MediaStreamSource instead to allow piping?
https://developers.google.com/gdata/javadoc/com/google/gdata/data/media/MediaStreamSource
That way I'm able to pipe the files directly like so
YouTubeService youTubeService = new YouTubeService("My-Service", developerKey);
youTubeService.setUserCredentials(user, password);
VideoEntry newEntry = new VideoEntry();
YouTubeMediaGroup mg = newEntry.getOrCreateMediaGroup();
mg.setTitle(new MediaTitle());
mg.getTitle().setPlainTextContent("Song Title");
mg.addCategory(new MediaCategory(YouTubeNamespace.CATEGORY_SCHEME, "Category"));
mg.setKeywords(new MediaKeywords());
mg.getKeywords().addKeyword("Test");
mg.setDescription(new MediaDescription());
mg.getDescription().setPlainTextContent("Song Description");
mg.setPrivate(false);
MediaStreamSource ms = new MediaFileSource(new URL("http://www.somewebsite.com/video.mp4").openStream(), "video/mp4");
newEntry.setMediaStream(ms);
String uploadUrl = "http://uploads.gdata.youtube.com/feeds/api/users/default/uploads";
VideoEntry createdEntry = youTubeService.insert(new URL(uploadUrl), newEntry);
return createdEntry.getHtmlLink().getHref();
I encountered an error and had to edit my code as:
String title = "Test Video";
youTubeService.getRequestFactory().setHeader("Slug", title);
Thought I should mention because I didn't see this in your snippet. But the upload is taking an insanely long time. Any progress at your end with the upload?
is there a way to route microphone input through headset and use simultaneously the AUDIO output through smartphone's speaker ?
I've been watching around for hours now and I saw that it's clearly impossible on iOS but what about Android..
I'm using a Samsung Galaxy S4.
Here is a part of my code to route the audio output through the speaker even if the headset is plugged in :
AudioManager speakerOutput = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
speakerOutput.setMode(AudioManager.MODE_IN_CALL);
speakerOutput.setSpeakerphoneOn(true);
But then when I tried to control my app by the headset's microphone, nothing. So I tried with the smartphone's one and it's obviously working.
It don't seems like others AudioManager.MODEs route audio output only and leave the mic input to the headset. So what can I do now without having to build a new kernel, modify drivers, HAL, …etc ?
Thanks, C.
There is no way to loopback from headset Mic to Speaker if you do not change HAL or driver on Android. If u can change hardware, it possible works out.
If you use AudioManager.STREAM_RING, the audio should be routed to both the loudspeaker and the wired headset, just as when the ringtone is played for an incoming call (remove the setMode and setSpeakerphoneOn calls).
Yes, It's possible. I successfully do it a few days ago. There are 2 solutions for it:
Solution 1:
Suppose you are in a normal mood & Headset connected. We just need to call setPreferredDevice function to set your preferred audio input/output device. Our input source is already coming from the headset. So we only need to route the audio output source to the Phone Speaker. Here is my sample code by Media Player:
#RequiresApi(Build.VERSION_CODES.P)
private fun playAudioFromPhoneSpeaker(context: Context, audioResId: Int) {
val mMediaPlayer: MediaPlayer = MediaPlayer.create(context, audioResId)
val mCurrentDevice: AudioDeviceInfo? = mMediaPlayer.preferredDevice
val mDeviceSpeaker: AudioDeviceInfo? = getDeviceOutputSpeaker(context)
mDeviceSpeaker?.let { speaker ->
if (mCurrentDevice != speaker) {
val result: Boolean? = mMediaPlayer.setPreferredDevice(mDeviceSpeaker)
Log.i("PreferredDevice", "is set as speaker: $result")
}
}
mMediaPlayer.start()
mMediaPlayer.setOnCompletionListener { mediaPlayer ->
mediaPlayer.release()
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getDeviceOutputSpeaker(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { speaker ->
if (speaker.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
return speaker
}
}
return null
}
The positive thing about this solution is that we don't need to think about the input source. We only change the audio output source and it doesn't have any impact on the input source.
The problem is setPreferredDevice of MediaPlayer come from Android 9. So this solution is only supported from Android 9.
Solution 2:
In 2nd solution we turn on device speaker by Audio Manager. So after turn on the device speaker the audio input & output source will come to the device internal speaker & device mic. Now we have to route the mic to the headset. Sample code:
private val mAudioRecord: AudioRecord? = null
private fun RecordAudioFromHeadphone(context: Context) {
//Step 1: Turn on Speaker
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.isSpeakerphoneOn = true
//Step 2: Init AudioRecorder TODO for you
//Step 3: Route mic to headset
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setPreferredInputMic(getWiredHeadPhoneMic(this))
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun setPreferredInputMic(mAudioDeviceInfo: AudioDeviceInfo?) {
val result: Boolean? = mAudioRecord?.setPreferredDevice(mAudioDeviceInfo)
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getWiredHeadPhoneMic(context: Context): AudioDeviceInfo? {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val mAudioDeviceOutputList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
mAudioDeviceOutputList?.filterNotNull()?.forEach { device ->
if (device.type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
return device
}
}
return null
}
The positive think of this solution is supported from Android 6. And negative thing is it's little bit complex and you have to maintain the voice input source.
AudioRecorder, MediaRecorder, AudioTrack also support this kind of solution.
Thanks