I'm trying to play a video with the use of ExoPlayer API and by using the exoplayer library of version : 'com.google.android.exoplayer:exoplayer:2.8.1'. I want to play a video called video.mp4 which is in a folder called folder1 and this folder in inside the folder assets in the res (res/assets/folder1/video.mp4). I cannot get my code to play the video. Please help me.
My MainActivity.java:
package com.example.amandeep.example2;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity
{
SimpleExoPlayer video_player;
PlayerView player_screen;
DefaultTrackSelector track_selector;
DefaultBandwidthMeter band_width_meter = new DefaultBandwidthMeter();
MediaSource mediaSource;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
player_screen = findViewById (R.id.player_screen);
player_screen.requestFocus();
TrackSelection.Factory video_track_selection_factory = new AdaptiveTrackSelection.Factory(band_width_meter);
track_selector = new DefaultTrackSelector(video_track_selection_factory);
video_player = ExoPlayerFactory.newSimpleInstance(this, track_selector);
player_screen.setPlayer(video_player);
video_player.setPlayWhenReady(true);
DataSource.Factory data_source_factory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "Application Name"), new DefaultBandwidthMeter());
Uri url = Uri.parse("file:///android_asset/folder/video.mp4");
mediaSource = new ExtractorMediaSource.Factory(data_source_factory).createMediaSource(url);
video_player.prepare(mediaSource);
}
}
My activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.exoplayer2.ui.PlayerView
android:id="#+id/player_screen"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:use_controller="true" />
</android.support.constraint.ConstraintLayout>
Note: I want to use the Uri.parse method.
IF SOMEONE CANNOT UNDERSTAND MY QUESTION PROPERLY, PLEASE COMMENT BELOW.
IF URI.PARSE METHOD CANNOT USE FILES FROM ASSETS THEN PLEASE TELL ME
ExoPlayer 2.12 introduces the MediaItem class so you can do:
val firstVideoUri = Uri.parse("asset:///localfile.mp4")
val firstItem = MediaItem.fromUri(firstVideoUri)
player.addMediaItem(firstItem)
Note that the URI should start with asset:/// not assets:///
This is my code to play audio file, hope it helps you.
private void prepareExoPlayerFromAssetResourceFile(int current_file) {
exoPlayer = ExoPlayerFactory.newSimpleInstance(this, new DefaultTrackSelector((TrackSelection.Factory) null), new DefaultLoadControl());
exoPlayer.addListener(eventListener);
//DataSpec dataSpec = new DataSpec(uri);
//DataSpec dataSpec = new DataSpec(Uri.parse("asset:///001.mp3"));
DataSpec dataSpec = new DataSpec(Uri.parse("asset:///" + current_file +".mp3"));
final AssetDataSource assetDataSource = new AssetDataSource(this);
try {
assetDataSource.open(dataSpec);
} catch (AssetDataSource.AssetDataSourceException e) {
e.printStackTrace();
}
DataSource.Factory factory = new DataSource.Factory() {
#Override
public DataSource createDataSource() {
//return rawResourceDataSource;
return assetDataSource;
}
};
MediaSource audioSource = new ExtractorMediaSource(assetDataSource.getUri(),
factory, new DefaultExtractorsFactory(), null, null);
exoPlayer.prepare(audioSource);
initMediaControls();
}
Try a uri of this format:
file:///android_asset/
For your example that would need to be:
Uri url = Uri.parse("file:///android_asset/folder1/video.mp4");
I'm using similar code for playing video from my assets folder with ExoPlayer and it works good.
But I noticed you mentioned diferent folders in description and code. You are saying the folder in assets is named folder1 but in code you have folder.
So make sure you have correct path to video.
I write this answer because some methods on old answers deprecated :
fun prepareExoPlayerFromAssetResource(
uri: String = "asset://example.mp3")
{
val dataSourceFactory = DataSource.Factory {AssetDataSource(context)}
val mediaSource = ProgressiveMediaSource
.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(uri)))
exoPlayer.addMediaSource(audioSource)
exoPlayer.prepare()
exoPlayer.play()
}
I hope this is useful for someone :)
This may be useful:
import android.annotation.SuppressLint
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.*
import com.google.android.exoplayer2.upstream.DataSource.Factory
import com.google.android.exoplayer2.util.Util
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity(), Player.EventListener {
private var player: SimpleExoPlayer? = null
private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
public override fun onStart() {
super.onStart()
if (Util.SDK_INT > 23) {
initializePlayer("assets:///your_file.your_extension") //"assets:///pillwheel_pills.mp4"
}
}
public override fun onResume() {
super.onResume()
hideSystemUi()
if (Util.SDK_INT <= 23 || player == null) {
initializePlayer("assets:///your_file.your_extension") //"assets:///pillwheel_pills.mp4"
}
}
public override fun onPause() {
super.onPause()
if (Util.SDK_INT <= 23) {
releasePlayer()
}
}
public override fun onStop() {
super.onStop()
if (Util.SDK_INT > 23) {
releasePlayer()
}
}
private fun initializePlayer(path: String) {
if (player == null) {
val trackSelector = DefaultTrackSelector(this)
trackSelector.setParameters(
trackSelector.buildUponParameters().setMaxVideoSizeSd())
player = SimpleExoPlayer.Builder(this)
.setTrackSelector(trackSelector)
.build()
}
video_view?.player = player
video_view?.requestFocus()
val dataSourceFactory = Factory { AssetDataSource(this#MainActivity) }
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.parse(path))
player?.playWhenReady = playWhenReady
player?.seekTo(currentWindow, playbackPosition)
player?.addListener(this)
player?.prepare(videoSource)
}
private fun releasePlayer() {
if (player != null) {
playbackPosition = player?.currentPosition!!
currentWindow = player?.currentWindowIndex!!
playWhenReady = player?.playWhenReady!!
player?.removeListener(this)
player?.release()
player = null
}
}
/**
* set fullscreen
*/
#SuppressLint("InlinedApi")
private fun hideSystemUi() {
video_view?.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
}
override fun onPlayerError(error: ExoPlaybackException) {
super.onPlayerError(error)
//handle the error
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
val stateString: String = when (playbackState) {
ExoPlayer.STATE_IDLE -> "ExoPlayer.STATE_IDLE -"
ExoPlayer.STATE_BUFFERING -> "ExoPlayer.STATE_BUFFERING -"
ExoPlayer.STATE_READY -> "ExoPlayer.STATE_READY -"
ExoPlayer.STATE_ENDED -> "ExoPlayer.STATE_ENDED -"
else -> "UNKNOWN_STATE -"
}
Log.d(TAG, "changed state to " + stateString
+ " playWhenReady: " + playWhenReady)
}
companion object {
private val TAG = MainActivity::class.java.name
}
}
You can find the full source code on GitHub
Related
I have created an app to record the audio in the android wear but I am not able to transfer that recorded file to mobile phone as there is no dedicated file manager in Samsung galaxy watch 4. So for that I need to upload the the recorded file to the AWS S3 STORAGE.
Here is the code for audio recording.
package com.example.watch
import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Intent
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.net.Uri
import android.os.*
import android.provider.MediaStore
import android.util.Log
import android.view.WindowManager
import android.widget.Button
import android.widget.Chronometer
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.amplifyframework.core.Amplify
import com.amplifyframework.storage.StorageException
import com.amplifyframework.storage.result.StorageUploadFileResult
import com.example.watch.databinding.ActivityMicrophoneBinding
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.*
#Suppress("DEPRECATION")
class Microphone : AppCompatActivity() {
// Initializing all variables..
private var Stop: Button? = null
private var Start: Button? = null
private var statusTV:TextView? = null
private var Chronometer:TextView?=null
var fileName: String? = null
var audioRecorder: MediaRecorder? = null
var audiouri: Uri? = null
var file: ParcelFileDescriptor? = null
lateinit var status:TextView
private var binding: ActivityMicrophoneBinding? = null
private lateinit var chronometer: Chronometer
private var audioFile: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_microphone)
if (!CheckPermissions())
RequestPermissions()
AmplifyInit().intializeAmplify(this#Microphone)
this.window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
)
supportActionBar!!.hide()
// initialize all variables with their layout items.
Start=binding!!.accelerometerStartButton
Stop=binding!!.accelerometerStopButton
status=binding!!.Status
chronometer=binding!!.chronometer
Start!!.setOnClickListener { // start recording method will
// start the recording of audio.
startRecording()
Start!!.isEnabled=false
status.text="Recording..."
chronometer.base = SystemClock.elapsedRealtime()
chronometer.start()
startService()
}
Stop!!.setOnClickListener { // pause Recording method will
// pause the recording of audio.
pauseRecording()
Start!!.isEnabled=true
status.text="Recording Stopped"
chronometer.stop()
uploadFile()
stopService()
}
}
private fun startRecording() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
val values = ContentValues(4)
values.put(MediaStore.Audio.Media.TITLE, fileName)
values.put(
MediaStore.Audio.Media.DATE_ADDED,
SimpleDateFormat("yyyyMMddHHmmss").format(Date())
)
values.put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/Recordings/")
audiouri = contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values)
file = contentResolver.openFileDescriptor(audiouri!!, "w")
if (file != null) {
audioRecorder = MediaRecorder()
audioRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
audioRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
audioRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
audioRecorder!!.setOutputFile(file!!.fileDescriptor)
audioRecorder!!.setAudioChannels(1)
audioRecorder!!.prepare()
audioRecorder!!.start()
audioRecorder!!.setOutputFile(getAudioFile().absolutePath)
}
}
else {
RequestPermissions()
}
}
private fun getAudioFile(): File {
if (audioFile == null) {
audioFile = File(getExternalFilesDir(null), "Music/Recordings/")
}
return audioFile!!
}
#SuppressLint("MissingSuperCall")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray,
) {
// this method is called when user will
// grant the permission for audio recording.
when (requestCode) {
REQUEST_AUDIO_PERMISSION_CODE -> {
if (grantResults.isNotEmpty()) {
val permissionToRecord = grantResults[0] == PackageManager.PERMISSION_GRANTED
val permissionToStore = grantResults[1] == PackageManager.PERMISSION_GRANTED
if (permissionToRecord && permissionToStore) {
Toast.makeText(applicationContext, "Permission Granted", Toast.LENGTH_LONG)
.show()
} else {
Toast.makeText(applicationContext, "Permission Denied", Toast.LENGTH_LONG)
.show()
}
}
}
}
}
private fun CheckPermissions(): Boolean {
// this method is used to check permission
val result = ContextCompat.checkSelfPermission(applicationContext, permission.WRITE_EXTERNAL_STORAGE)
val result1 = ContextCompat.checkSelfPermission(applicationContext, permission.RECORD_AUDIO)
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED
}
private fun RequestPermissions() {
// this method is used to request the
// permission for audio recording and storage.
ActivityCompat.requestPermissions(this,
arrayOf(permission.RECORD_AUDIO, permission.WRITE_EXTERNAL_STORAGE),
REQUEST_AUDIO_PERMISSION_CODE
)
}
private fun pauseRecording() {
Start!!.isEnabled=true
stopService(Intent(this, ForegroundService::class.java))
// below method will stop
// the audio recording.
try {
audioRecorder!!.stop()
} catch (stopException: RuntimeException) {
// handle cleanup here
}
}
fun startService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
serviceIntent.putExtra("inputExtra", "Foreground Service Example in Android")
ContextCompat.startForegroundService(this, serviceIntent)
}
fun stopService() {
val serviceIntent = Intent(this, ForegroundService::class.java)
stopService(serviceIntent)
}
companion object {
// string variable is created for storing a file name
private var mFileName: String? = null
var permissionAccepted = false
var permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO,
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
// constant for storing audio permission
const val REQUEST_AUDIO_PERMISSION_CODE = 1000
}
private fun uploadFile() {
Amplify.Storage.uploadFile(
"Audio/audio.mp3",
getAudioFile(),
{ result: StorageUploadFileResult ->
Log.i(
"MyAmplifyApp",
"Successfully uploaded: " + result.key
)
Toast.makeText(this, "File has Successfully Uploaded:" , Toast.LENGTH_SHORT).show()
}
) { storageFailure: StorageException? ->
Log.e(
"MyAmplifyApp",
"Upload failed",
storageFailure
)
Toast.makeText(this, "Upload failed", Toast.LENGTH_SHORT).show()
}
}
}
Now how can i modify my uploadfile function to upload the fiole to AWS S3, As i am getting this error.
E/amplify:aws-s3-storage:SinglePartUploadWorker: SinglePartUploadWorker failed with exception: kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=JobImpl{Cancelled}#9b17179
I/WM-WorkerWrapper: Worker result RETRY for Work [ id=48cd3651-35e2-456e-9329-81930f5277a5, tags={ UPLOAD, awsS3StoragePlugin, 9, com.amplifyframework.storage.s3.transfer.worker.RouterWorker } ]
I am using dependecies:
implementation 'com.amplifyframework:aws-api:2.1.0'
implementation 'com.amplifyframework:aws-datastore:2.1.0'
implementation 'com.amplifyframework:aws-storage-s3:2.1.0'
implementation 'com.amplifyframework:aws-auth-cognito:2.1.0'
Library:
<uses-library
android:name="com.google.android.wearable"
android:required="true" />
when i build smaple project , app has been crashed
when i see logcat , i see this:
90-30676/io.agora.agora_android_uikit E/agora.io: jni_generator_helper.h: (line 131): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
what do i do ?
My code:
package io.agora.agora_android_uikit
import android.Manifest
import android.graphics.Color
import android.os.Bundle
import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import io.agora.agorauikit_android.AgoraButton
import io.agora.agorauikit_android.AgoraConnectionData
import io.agora.agorauikit_android.AgoraSettings
import io.agora.agorauikit_android.AgoraVideoViewer
import io.agora.agorauikit_android.requestPermission
import io.agora.rtc2.Constants
private const val PERMISSION_REQ_ID = 22
private val REQUESTED_PERMISSIONS = arrayOf<String>(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
#ExperimentalUnsignedTypes
class MainActivity : AppCompatActivity() {
var agView: AgoraVideoViewer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
try {
agView = AgoraVideoViewer(
this, AgoraConnectionData("*******"),
)
} catch (e: Exception) {
println("Could not initialise AgoraVideoViewer. Check your App ID is valid. ${e.message}")
return
}
val set = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
this.addContentView(agView, set)
if (AgoraVideoViewer.requestPermission(this)) {
agView!!.join("test", role = Constants.CLIENT_ROLE_BROADCASTER)
} else {
val joinButton = Button(this)
// this#MainActivity.runOnUiThread(java.lang.Runnable {
runOnUiThread {
Runnable { joinButton.text = "Allow Camera and Microphone, then click here" }
}
// })
joinButton.setOnClickListener {
if (AgoraVideoViewer.requestPermission(this)) {
// (joinButton.parent as ViewGroup).removeView(joinButton)
agView!!.join("test", role = Constants.CLIENT_ROLE_BROADCASTER)
}
}
// joinButton.setBackgroundColor(Color.GREEN)
// joinButton.setTextColor(Color.RED)
this.addContentView(
joinButton,
FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, 300)
)
}
}
i want to solve my issue
Please wrap with function and make annotations there instead of mentioning in the main content view and wrap using UI thread.And refer sample github agora sdk github for reference - https://github.com/AgoraIO-Community/VideoUIKit-Android
I am trying to pre-cache/pre-buffer HLS videos to my app. I used CacheWriter to cache the (.mp4) file, But it was not able to cache segments of HLS video. Basically, I have only URL of the master playlist file which has media playlists of different qualities, and that each media playlist has segments (.ts).
So, I have to cache the master playlist and any one media playlist and then some segments and play the cached media to Exoplayer. how can I cache these?
I also visited https://github.com/google/ExoPlayer/issues/9337 But this does not have any example to do so.
This is how I cached .mp4 by CacheWriter
CacheWriter cacheWriter = new CacheWriter( mCacheDataSource,
dataSpec,
null,
progressListener);
cacheWriter.cache();
I am answering my own question for further users struggling on it.
We can pre-cache pre-cache HLS adaptive stream in ExoPlayer By using HlsDownloader provided by Exoplayer.
Add this Kotlin class to your project ExoPlayerModule.kt.
//SitaRam
package com.example.youtpackagename
import android.content.Context
import android.util.Log
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.source.hls.offline.HlsDownloader
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.FileDataSource
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
import com.google.android.exoplayer2.upstream.cache.SimpleCache
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.util.concurrent.CancellationException
//bytes to be downloaded
private const val PRE_CACHE_AMOUNT = 2 * 1048576L
class ExoPlayerModule(context: Context) {
private var cronetDataSourceFactory = DefaultHttpDataSource.Factory()
//StaticMember is class which contains cookie in my case, you can skip cookies and use DefaultHttpDataSource.Factory().
/*val Cookie = mapOf("Cookie" to StaticMember.getCookie())
private var cronetDataSourceFactory = if (StaticMember.getCookie() != null) {
DefaultHttpDataSource.Factory().setDefaultRequestProperties(Cookie)
}else {
DefaultHttpDataSource.Factory()
}*/
private val cacheReadDataSourceFactory = FileDataSource.Factory()
private var cache = simpleCache.SimpleCache(context)
private var cacheDataSourceFactory = CacheDataSource.Factory()
.setCache(cache)
// .setCacheWriteDataSinkFactory(cacheSink)
.setCacheReadDataSourceFactory(cacheReadDataSourceFactory)
.setUpstreamDataSourceFactory(cronetDataSourceFactory)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
fun isUriCached(uri: String, position: Long = 0): Boolean {
return cache.isCached(uri, position, PRE_CACHE_AMOUNT)
}
//updating cookies (if you are using cookies).
/* fun updateDataSourceFactory(){
val Cookie = mapOf("Cookie" to StaticMember.getCookie())
cronetDataSourceFactory = if (StaticMember.getCookie() != null) {
DefaultHttpDataSource.Factory().setDefaultRequestProperties(Cookie)
}else {
DefaultHttpDataSource.Factory()
}
cacheDataSourceFactory = CacheDataSource.Factory()
.setCache(cache)
// .setCacheWriteDataSinkFactory(cacheSink)
.setCacheReadDataSourceFactory(cacheReadDataSourceFactory)
.setUpstreamDataSourceFactory(cronetDataSourceFactory)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
}*/
// TODO add the same for mp4. Also they might be a much better option, since they only have
// single track, so no matter what connection you have - loading can't happen twice
fun getHlsMediaSource(mediaItem: MediaItem): HlsMediaSource {
return HlsMediaSource.Factory(cacheDataSourceFactory)
.setAllowChunklessPreparation(true)
.createMediaSource(mediaItem)
}
fun releaseCache() = cache.release()
suspend fun preCacheUri(mediaItem: MediaItem) {
val downloader = HlsDownloader(mediaItem, cacheDataSourceFactory)
withContext(Dispatchers.IO) {
try {
downloader.download { _, bytesDownloaded, _ ->
if (MainActivity.nextUrl==mediaItem){
// Log.e("bytesCaching", "while: same $mediaItem same")
}else {
// Log.e("bytesCaching", "while: $mediaItem")
downloader.cancel()
}
if (bytesDownloaded >= PRE_CACHE_AMOUNT) {
// log("video precached at $percent%")
downloader.cancel()
}
}
} catch (e: Exception) {
if (e !is CancellationException) log("precache exception $e")
}
}
}
private fun log(s: String) {
TODO("Not yet implemented")
}
}
Initializing ExoPlayerModule
ExoPlayerModule PlayerModuleO = new ExoPlayerModule(MainActivity.this);
For Pre-Loading.
String previousUrl = "";
public void preLoad(String url) {
if (previousUrl.equals(url)) {
return;
}
previousUrl = url;
MediaItem mediaItem =MediaItem.fromUri(Uri.parse(url));
PlayerModuleO.preCacheUri(mediaItem, new Continuation<>() {
#NonNull
#Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
#Override
public void resumeWith(#NonNull Object o) {
}
});
}
Playing cached or non-cached media.
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(url));
exoPlayer.setMediaSource(PlayerModuleO.getHlsMediaSource(mediaItem));
exoPlayer.prepare();
exoPlayer.play();
Releasing cache
PlayerModuleO.releaseCache();
If you are having any problems then feel free to ask.
Iam and AR android app. I can load my .sfb files from asset. i want to load from direct server in order to secure my assets. Its loading from asset folder. not from direct server. iam using below code pls help me to solve this.
package com.example.a320_ar
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import com.google.ar.core.Anchor
import com.google.ar.core.Plane
import com.google.ar.sceneform.AnchorNode
import com.google.ar.sceneform.HitTestResult
import com.google.ar.sceneform.SkeletonNode
import com.google.ar.sceneform.animation.ModelAnimator
import com.google.ar.sceneform.rendering.ModelRenderable
import com.google.ar.sceneform.ux.ArFragment
import com.google.ar.sceneform.ux.TransformableNode
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
class MainActivity : AppCompatActivity() {
lateinit var arFragment: ArFragment
private lateinit var model: Uri
private var rendarable: ModelRenderable?=null
private var animator: ModelAnimator? = null
//private var modellink:String = "A320_Anim.sfb"
private var modellink:String = "http://10.0.0.193:90/fbx/A320_Anim.sfb"
#RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
arFragment = sceneform_fragment as ArFragment
arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING) {
return#setOnTapArPlaneListener
}
var anchor = hitResult.createAnchor()
btnStart.setOnClickListener {
placeObject(
arFragment,
anchor,
Uri.parse(modellink)
)
}
}
}
#RequiresApi(Build.VERSION_CODES.N)
private fun animateModel(name: String) {
animator?.let { it->
if(it.isRunning){
it.end()
}
}
rendarable?.let { modelRenderable ->
val data = modelRenderable.getAnimationData(name)
animator = ModelAnimator(data,modelRenderable)
animator?.start()
}
}
#RequiresApi(Build.VERSION_CODES.N)
private fun placeObject(arFragment: ArFragment, anchor: Anchor?, model: Uri?) {
ModelRenderable.builder()
.setSource(arFragment.context,model)
.build()
.thenAccept{
rendarable = it
addtoScene(arFragment, anchor, it)
}
.exceptionally {
val builder = AlertDialog.Builder(this)
builder.setMessage( it.message).setTitle("Error")
val dialog = builder.create()
dialog.show()
return#exceptionally null
}
}
private fun addtoScene(arFragment: ArFragment, anchor: Anchor?, it: ModelRenderable?) {
val anchorNode = AnchorNode(anchor)
val skeletonNode = SkeletonNode()
skeletonNode.renderable = rendarable
Toast.makeText(this,"inside add scene",Toast.LENGTH_SHORT).show()
val node = TransformableNode(arFragment.transformationSystem)
node.addChild(skeletonNode)
node.setParent(anchorNode)
node.setOnTapListener { v: HitTestResult?, event: MotionEvent? ->
//msgText.text = "Tapped me...$anchorNode --- $anchor --- $skeletonNode"
// var bt = findViewById<Button>(R.id.btnDel)
//bt.visibility = View.VISIBLE
//removeAnchorNode(anchorNode)
//bt.setOnClickListener { removeAnchorNode(anchorNode) }
}
arFragment.arSceneView.scene.addChild(anchorNode)
}
}
its working fine no errors. but its not showing my object on fragment.
private var modellink:String = "http://10.0.0.193:90/fbx/A320_Anim.sfb" (not loading .sfb)
private var modellink:String = "A320_Anim.sfb" (Loading the .sfb- woring fine)
please help me to load the model directly from server. i used all the permissions correctly.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
Thanks in advance,
Syed Abdul Rahim
Are you still using the old 1.6 version of Sceneform? You could try the new maintained version and also discard the sfb format and switch to glTF. The maintained version is up to date related to android dependencies and ARCore/Filament.
Well to your second question, if you want to secure your assets you have to serve it from a password protected API-Endpoint, but you have to host your own server, maybe some simpler solutions exists. Strings or text files can be directly secured with on board libraries (https://developer.android.com/guide/topics/security/cryptography)
I am able to tun my test but it fails. The problem is, mocked method is still returning wrong data. This is my method that I want to test it:
fun getTextByLanguage(list: List<TitleModel>) : String {
val deviceLanguage = Locale.getDefault().language
var default = ""
for (item in list) {
if (item.culture.contains(deviceLanguage, true)) return item.value
if (item.culture.contains("en", true)) default = item.value
}
return default
}
And this is how I am testing the method:
import junit.framework.TestCase.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.powermock.api.mockito.PowerMockito.*
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import java.util.Locale
#RunWith(PowerMockRunner::class)
#PrepareForTest(Locale::class)
class AppConfigUtilityByPowerMockTest {
#Test
fun `getTextByLanguage, test en`() {
mockStatic(Locale::class.java)
val mockedLocal = mock(Locale::class.java)
`when`(Locale.getDefault()).thenReturn(mockedLocal)
`when`(mockedLocal.language).thenReturn("en")
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list)
assertEquals("Home", actual)
}
#Test
fun `getTextByLanguage, test fr`() {
mockStatic(Locale::class.java)
val mockedLocal = mock(Locale::class.java)
`when`(Locale.getDefault()).thenReturn(mockedLocal)
`when`(mockedLocal.language).thenReturn("fr")
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list)
assertEquals("HomeFr", actual)
}
}
The first test cases without problem but the second one fails. This is the out put:
junit.framework.ComparisonFailure: expected:<Home[Fr]> but was:<Home[]>
Expected :HomeFr
Actual :Home
One way to answer the question is to get rid of Local class from the method and do stuff in Kotlin way. So, what I did is to change my original method like this:
fun getTextByLanguage(list: List<TitleModel>, deviceLanguage: String = getDeviceLanguage()) : String {
var default = ""
for (item in list) {
if (item.culture.contains(deviceLanguage, true)) return item.value
if (item.culture.contains("en", true)) default = item.value
}
return default
}
fun getDeviceLanguage(): String {
return Locale.getDefault().language
}
Now my test class looks like this:
import junit.framework.TestCase.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import java.util.Locale
#RunWith(PowerMockRunner::class)
#PrepareForTest(Locale::class)
class AppConfigUtilityByPowerMockTest {
#Test
fun `getTextByLanguage, test en`() {
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list, "en")
assertEquals("Home", actual)
}
#Test
fun `getTextByLanguage, test fr`() {
val list = listOf(TitleModel("en-ca", "Home"), TitleModel("fr-ca", "HomeFr"))
val actual = getTextByLanguage(list, "fr")
assertEquals("HomeFr", actual)
}
}
Although my test passes, I am still willing to see your recommendation and down to accept your answer to my question.