how to stream a mp3 audio from firebase storage - firebase

I'm developing an app that will stream mp3 file stored in firebase storage. Nearly I'm having 100 songs like song1,song2,song3..... if a song is selected I have to stream that particular song without downloading. In such case I need to write plenty of code because for each song I have to mention the firebase storage url. the url would be like
https://firebasestorage.googleapis.com/......song1.mp3?alt=media&token=8a8a0593-e8bb-40c7-87e0-814d9c8342f3
For each song the alt=media&token= part of the url varies, so I have to mention the unique url for all songs. But here I need some simplified coding to play the songs by mentioning its name alone from firebase storage.
Please suggest a way to stream the audio file by using its name alone that is stored in firebase storage.

You have two choices if you want to get files out of a Firebase Storage bucket.
Use the full download url that you can get from a Storage Reference that points to the file path in your bucket.
Use a download task (Android or iOS) to fetch the data from the file.
You can't get the file data any other way from within a mobile app. Firebase Storage doesn't support special media streaming protocols, such as RTSP.

I did it using downloadUrl.
val storage = FirebaseStorage.getInstance()
storage.reference.child("songs/song1.mp3").downloadUrl.addOnSuccessListener({
val mediaPlayer = MediaPlayer()
mediaPlayer.setDataSource(it.toString())
mediaPlayer.setOnPreparedListener { player ->
player.start()
}
mediaPlayer.prepareAsync()
})

StorageReference filepath=storage.child("Audio").child(timeStamp+".mp3");
Uri uri=Uri.fromFile(new File(fileName));
filepath.putFile(uri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
String audio_url=taskSnapshot.getDownloadUrl().toString();
Use audio_urlto stream the audio file using media player

Related

Firebase Cloud Storage - Unity - PutStreamAsync memory crash with large files

I'm working on a Unity3D application connected to a Firebase backend.
We're using Auth, Firestore and Storage, the connection to Firebase is working smoothly.
When trying to upload a large file in iOS, a video recording of 200mb+, the app starts using all memory out of nowhere and crashes.
I'm using the PutStreamAsync method as described in the docs.
https://firebase.google.com/docs/storage/unity/upload-files#upload_from_a_local_file
I also tried using PutFileAsync but I'm getting a ErrorObjectNotFound, which makes no sense to me because it's an upload, not a download.
Error codes: https://firebase.google.com/docs/storage/unity/handle-errors
Here's our code. Just a simple use case, very similar to what's shown in the docs:
public async Task<string> UploadTestVideo(TestExecution testExecution)
{
string videoPath = TestPathHelper.GetLocalVideoPath(testExecution);
StorageReference folderRef = storageReference.Child($"{StoragePathTestExecutions}/{testExecution.UID}/{StoragePathVideoRecordings}");
StorageReference fileRef = folderRef.Child("recording.mp4");
TVLog.Debug(Tag, $"Upload video <{videoPath}> to CloudStorage at <{fileRef.Path}>");
try
{
MetadataChange newMetadata = new MetadataChange();
newMetadata.ContentType = "video/mp4";
//StreamReader stream = new StreamReader(videoPath);
FileStream stream = File.OpenRead(videoPath);
TVLog.Debug(Tag, "Opened file stream for uploading...");
StorageMetadata metadata = await fileRef.PutStreamAsync(stream, newMetadata);
TVLog.Debug(Tag, "Finished uploading video...");
stream.Close();
stream.Dispose();
// Metadata contains file metadata such as size, content-type, and download URL.
string md5Hash = metadata.Md5Hash;
TVLog.Debug(Tag, "video md5 hash = " + md5Hash);
return md5Hash;
}
catch(StorageException e){
TVLog.Exception(Tag, $"Exception uploading video {e.HttpResultCode} - {e.ErrorCode} - {e.Message}");
return null;
}
}
XCode shows how memory spikes from 300mb to 1.4gb in a few seconds and it crashes.
When trying with a 100mb video, memory goes from 300 to 800mb and the upload succeeds, which confirms that the code is working and doing what's supposed to do.
But when we try with 200mb files, memory goes way beyond that and clearly the OS kills the app.
This is what I see in the XCode log when the crash happens:
WARNING -> applicationDidReceiveMemoryWarning()
2022-07-28 16:13:15.059747-0300 MyProject[84495:5466165] [xpc] <PKDaemonClient: 0x282907f00>: XPC error talking to pkd: Connection interrupted
WARNING -> applicationDidReceiveMemoryWarning()
2022-07-28 16:13:15.189228-0300 MyProject[84495:5466490] [ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
WARNING -> applicationDidReceiveMemoryWarning()
I'm using Unity 2020.3.18f1 LTS and Firebase SDK for Unity 8.10.1, installed through package manager. We can't upgrade because the newer versions are compiled by a version of XCode not yet supported by Unity Cloud Build (yeah that's really sad).
Is there anything I could do on my side or is this clearly a bug in the SDK?
Will be trying to find alternatives in the meantime.
Finally I managed to effectively use PutFileAsync.
Documentation is misleading. The name of the parameter is "filePath" and in the docs for the Unity SDK there's a sample that uses a normal file path like "folder/file.png".
It turns out that it's not a path, it's an URI, and I had to prepend "file://" in order to make it work. After that, I could upload 500mb files with no issues.
In the API specification, under the "filePath" parameter, it explains that it is an URI.
https://firebase.google.com/docs/reference/unity/class/firebase/storage/storage-reference#class_firebase_1_1_storage_1_1_storage_reference_1a0b6fee3e69ca0b004e8675f7d867d925
I wonder why they don't name it differently or add a proper example in the docs. Lost hours with this. Anyways, happy it's solved.

Does loading Firebase Storage image with Glide generate billing expenses?

I would like to know if this action that I am doing in my application generates expenses for Firebase Storage. That is,
if regardless of the times I do the following action it will generate some expense in Firebase billing.
I have images stored in storage, and I upload these images to an ImageView using the Glide library, like this:
Uri url = Uri.parse(pictureUser);
Glide.with(MainActivity.this).load(url).into(imageView);
I have a chat application, and in each message on the adapter I want to upload the user's photo this way, using Glide, so, as there are many uploads, I wanted to know if every time I run this code snippet, it generates some billing expenses of the firebase.
As you are using this in chat activity this means that same image may probably load many of times.
To decrease the number of images that download I suggest you to use instead of:
Glide.with(MainActivity.this).load(url).into(imageView);
replace it by :
Glide.with(MainActivity.this)
.load(url).diskCacheStrategy(DiskCacheStrategy.DATA)
.into(imageView);
by this way, you are activating the cache mode. this means the Glide will search in the cache if it exists then will upload it from the internet.
I advise you to use Picasso instead of Glide. it works better in mode offline than Glide:
Picasso.with(MainActivity.this)
.load(url)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
// Try again online if cache failed
Picasso.with(MainActivity.this)
.load(url)
.error(R.drawable.user_placeholder_error)
.into(imageView);
}
});
Any time your code download a file, it will be billed as egress from Cloud Storage.
With Glide, it as a disk cache enabled by default, so it should only download a URL once and reuse it from cache (as long as it remains in cache). That would not be billed, since it's not downloading anything from the cloud.

Going offline with app and database syncing

I have a NativeScript (Angular) app that makes API-calls to a server to get data. I want to implement a bi-directional synchronization once a device gets online but using current API, no BaaS.
I could do a sort of caching. Once in a while app invalidates info in database and fetches it again. I don't like this approach because there are big lists that may change. They are fetched in batches, i.e. by page. One of them is a list of files downloaded to and stored on a device. So I have to keep those that are still in the list, and delete those that are not. It sounds like a nightmare.
How would you solve such a problem?
I use nativescript-couchebase plugin to store the data. We have following services
Connectivity
Data
API Service
Based on connectivity is Online/Offline, we either fetch data from remote API or via couchebase db. Please note that API service always returns the data from Couchebase only.
So in online mode
API Call -> Write to DB -> Return latest data from Couchebase
Offline mode
Read DB -> Return latest data from Couchebase
Also along with this, we maintain all API calls in a queue. So whenever connectivity returns, API calls are processed in sequence. Another challenge that you may face while coming in online mode from offline mode is the token expiry. This problem can be solved by showing a small popup to user after you come online.
I do this by saving my data as a json string and saving it to the devices file system.
When the app loads/reloads I read it from the file.
ie.
const fileSystemModule = require("tns-core-modules/file-system");
var siteid = appSettings.getNumber("siteid");
var fileName = viewName + ".json";
const documents = fileSystemModule.knownFolders.documents();
const site_folder = documents.getFolder("site");
const siteid_folder = site_folder.getFolder(siteid.toString());
const directoryPath = fileSystemModule.path.join(siteid_folder.path, fileName);
const directoryFile = fileSystemModule.File.fromPath(directoryPath);
directoryFile.writeText(json_string)
.then((result) => {
directoryFile.readText().then((res) => {
retFun(res);
});
}).catch((err) => {
console.log(err.stack);
});

Firebase Push keys as Firebase Storage file names?

I noticed that to use Firebase Storage (Google Cloud Storage) I need to come up with a unique file name to upload a file.
I then plan to keep a copy of that Storage file location (https URL or gs URL) in the Firebase Realtime database, where the clients will be able to read and download it separately
However I am unable to come up with unique filenames for the files located on Firebase Storage. Using a UUID generator might cause collisions in my case since several clients are uploading images to a single Firebase root
Here's my plan. I'd like to know if it will work
Lets call my firebase root : Chatrooms, which consists of keys : chatroom_1, chatroom_2 ...chatroom_n
under chatroom_k I have a root called "Content", which stores Push keys that are uniquely generated by Firebase to store content. Each push key represents a content, but the actual content is stored in Firebase Storage and a key called URL references the URL of the actual content. Can the filename for this content on Firebase storage have the same randomized Push key as long as the bucket hierarchy represents chatroom_k?
I am not sure if storage provides push() function but a suggestion would be the following:
Request a push() to a random location to your firebase database and use this key for a name.
At any case you will probably need to store this name to the database too.
In my application I have a node called "photos" and there I store the information about the images I upload. I first do a push() to get a new key and I use this key to rename the uploaded image to.
Is this what you need or I misunderstood something?
So I had the same problem and I reached this solution:
I named the files with time and date and the user uid, so it is almost impossible to have two files with the same name and they will be different every single time.
DateFormat dtForm = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss.");
String date = dtForm.format(Calendar.getInstance().getTime());
String fileName = date + FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseStorage
.getInstance()
.getReference("Folder/SubFolder/" + fileName)
.putFile(yourData)
With this the name of the files are going to be like this "2022.09.12.11.50.59.WEFfwds2234SA11" for example

Firebase storage slow when delivering public images

I'm using firebase storage to upload avatars (original.jpg).
In a node.js background process I resize them with gm and then put them back in my firebase bucket. These pictures are publicly readable, see my storage rules :
service firebase.storage {
match /b/xxx.appspot.com/o {
match /images {
match /{allImages=**} {
allow read;
}
match /{userId}/avatar/original.jpg {
allow write: if request.auth.uid == userId;
}
}
}
}
I store the url of images in firebase database ex:
https://firebasestorage.googleapis.com/v0/b/xxx.appspot.com/o/images%2Fyyy%2Favatar%2Fzzz-small.jpg?alt=media
That I retrieved right after the file upload (web sdk) var downloadURL = uploadTask.snapshot.downloadURL; ; stripped off the token parameter as it's gonna be public ; and where zzz-small.jpg has replaced original.jpg.
It works, pictures are displayed.
But it's slow even though the picture is a 40x40px jpg. And it hangs for a while before actually downloading the file, see Chrome Network record :
Chrome network record
The file is 158B, there's a ~1s waiting before the download ~3ms...
Is firebase storage supposed to be as fast as a CDN ?
Any faster way to call a public readable file ?
[EDIT]
Got some feedback from Firebase support :
We're definitely aware that many users, such as yourself, are having
issues with slow downloading of file from Europe. We're exploring
potential solutions, but I can't share any details or timelines at
this time. We'll keep your feedback in consideration moving forward
though.
Keep an eye out on our release notes for any further updates.
I forgot to mention I'm in Europe and the storage in the US.

Resources