I use aws-android-sdk-1.4.3/samples/S3_SimpleDB_SNS_SQS_Demo to preview my files stored on Amazon (Amazon Simple Storage Service). Looking through code I saw that they use this, to acces the files:
com.amazonaws.demo.s3.S3.getDataForObject (line 130)
public static String getDataForObject( String bucketName, String objectName ) {
return read( getInstance().getObject( bucketName, objectName ).getObjectContent() );
}
protected static String read( InputStream stream ) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream( 8196 );
byte[] buffer = new byte[1024];
int length = 0;
while ( ( length = stream.read( buffer ) ) > 0 ) {
baos.write( buffer, 0, length );
}
return baos.toString();
}
catch ( Exception exception ) {
return exception.getMessage();
}
}
}
Well, I have modified this methods to return ByteArrayOutputStream instead then I easily transform it to String or Bitmap (applying ByteArrayOutputStream.toByteArray() then using
BitmapFactory.decodeByteArray(byte[] data, int offset, int length, Options opts)).
So, it works on text-files and pictures. My problem is when I try to access videos. So, my questions are:
1.Using the method provided above, how could I get a video from ByteArrayOutputStream (ByteArrayOutputStream.toString()) and play it in a VideoView or using MediaPlayer or an approach... ?
2 . Does anybody know any other solution to this problem of preview videos stored on Amazon ? (I heard that on their sdk for IOS they use URLs to access files...)
PS: Supplying the file URL and open it in browser does not make sense, because this URLs expire after a wile.
First we have to provide the name of our bucket and the object (see aws-android-sdk-1.4.3/samples/S3_SimpleDB_SNS_SQS_Demo for a complet guide) we want to open then get the URL to our object:
AWSCredentials myCredentials = new BasicAWSCredentials("YOUR_AMAZON_ACCESS_KEY_ID", "YOUR_AMAZON_SECRET_KEY_ID");
AmazonS3 s3client = new AmazonS3Client(myCredentials);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
URL objectURL = s3client.generatePresignedUrl(request);
Now, just play the video in a video view, supplying the URL obtained:
getWindow().setFormat(PixelFormat.TRANSLUCENT);
mediaCtrl = new MediaController(this);
mediaCtrl.setMediaPlayer(videoView);
videoView.setMediaController(mediaCtrl);
Uri clip = Uri.parse(objectURL.toString());
videoView.setVideoURI(clip);
videoView.requestFocus();
videoView.start();
I want to give thanks to #CommonsWare for
indicating me through REST API (even the code I used is from aws-sdk reading the REST API documentation helped me and show also other ways of requesting Amazon objects)
indicating me to use generatePresignedUrl()
the code for playing the video is also inspired from his materials.
1.Using the method provided above, how could I get a video from ByteArrayOutputStream (ByteArrayOutputStream.toString()) and play it in a VideoView or using MediaPlayer or an approach... ?
Maybe you could get it to work by publishing the byte array through a ContentProvider and openFile(). Here is a sample project where I demonstrate serving a file by means of a custom InputStream this way.
The media subsystem is rather fussy, though, and so I do not give you good odds on this working.
2 . Does anybody know any other solution to this problem of preview videos stored on Amazon ? (I heard that on their sdk for IOS they use URLs to access files...)
Last I checked, S3 had a REST API that you could use to generate URLs to the videos. I'd hand that URL to MediaPlayer or VideoView.
Supplying the file URL and open it in browser does not make sense, because this URLs expire after a wile.
But you control how long "a wile [sic]" is. Make it be 24 hours or something.
#AlexAndro answer
AWSCredentials myCredentials = new BasicAWSCredentials("YOUR_AMAZON_ACCESS_KEY_ID", "YOUR_AMAZON_SECRET_KEY_ID");
AmazonS3 s3client = new AmazonS3Client(myCredentials);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName);
URL objectURL = s3client.generatePresignedUrl(request);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
mediaCtrl = new MediaController(this);
mediaCtrl.setMediaPlayer(videoView);
videoView.setMediaController(mediaCtrl);
Uri clip = Uri.parse(objectURL.toString());
videoView.setVideoURI(clip);
videoView.requestFocus();
videoView.start();
solved my problem, but it needs to define your region
using
s3client.setRegion(Region.EU_Paris.toAWSRegion())
EU_Paris is for eu-west-3
here you can find all regions
Related
Background
We want to let the user choose a video from any app, and then trim a video to be of max of 5 seconds.
The problem
For getting a Uri to be selected, we got it working fine (solution available here) .
As for the trimming itself, we couldn't find any good library that has permissive license, except for one called "k4l-video-trimmer" . The library "FFmpeg", for example, is considered not permission as it uses GPLv3, which requires the app that uses it to also be open sourced. Besides, as I've read, it takes quite a lot (about 9MB).
Sadly, this library (k4l-video-trimmer) is very old and wasn't updated in years, so I had to fork it (here) in order to handle it nicely. It uses a open sourced library called "mp4parser" to do the trimming.
Problem is, this library seems to be able to handle files only, and not a Uri or InputStream, so even the sample can crash when selecting items that aren't reachable like a normal file, or even have paths that it can't handle. I know that in many cases it is possible to get a path of a file, but in many other cases, it's not, and I also know it's possible to just copy the file (here), but this isn't a good solution, as the file could be large and take a lot of space even though it's already accessible.
What I've tried
There are 2 places that the library uses a file:
In "K4LVideoTrimmer" file, in the "setVideoURI" function, which just gets the file size to be shown. Here the solution is quite easy, based on Google's documentation:
public void setVideoURI(final Uri videoURI) {
mSrc = videoURI;
if (mOriginSizeFile == 0) {
final Cursor cursor = getContext().getContentResolver().query(videoURI, null, null, null, null);
if (cursor != null) {
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
cursor.moveToFirst();
mOriginSizeFile = cursor.getLong(sizeIndex);
cursor.close();
mTextSize.setText(Formatter.formatShortFileSize(getContext(), mOriginSizeFile));
}
}
...
In "TrimVideoUtils" file, in "startTrim" which calls "genVideoUsingMp4Parser" function. There, it calls the "mp4parser" library using :
Movie movie = MovieCreator.build(new FileDataSourceViaHeapImpl(src.getAbsolutePath()));
It says that they use FileDataSourceViaHeapImpl (from "mp4parser" library) to avoid OOM on Android, so I decided to stay with it.
Thing is, there are 4 CTORS for it, all expect some variation of a file: File, filePath, FileChannel , FileChannel+fileName .
The questions
Is there a way to overcome this?
Maybe implement FileChannel and simulate a real file, by using ContentResolver and Uri ? I guess it might be possible, even if it means re-opening the InputStream when needed...
In order to see what I got working, you can clone the project here. Just know that it doesn't do any trimming, as the code for it in "K4LVideoTrimmer" file is commented:
//TODO handle trimming using Uri
//TrimVideoUtils.startTrim(file, getDestinationPath(), mStartPosition, mEndPosition, mOnTrimVideoListener);
Is there perhaps a better alternative to this trimming library, which is also permissive (meaning of Apache2/MIT licences , for example) ? One that don't have this issue? Or maybe even something of Android framework itself? I think MediaMuxer class could help (as written here), but I think it might need API 26, while we need to handle API 21 and above...
EDIT:
I thought I've found a solution by using a different solution for trimming itself, and wrote about it here, but sadly it can't handle some input videos, while mp4parser library can handle them.
Please let me know if it's possible to modify mp4parser to handle such input videos even if it's from Uri and not a File (without a workaround of just copying to a video file).
First of all a caveat: I am not familiar with the mp4parser library but your question looked interesting so I took a look.
I think its worth you looking at one of the classes the code comments say is "mainly for testing". InMemRandomAccessSourceImpl. To create a Movie from any URI, the code would be as follows:
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
Log.e("InputStream Size","Size " + inputStream);
int bytesAvailable = inputStream.available();
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
final byte[] buffer = new byte[bufferSize];
int read = 0;
int total = 0;
while ((read = inputStream.read(buffer)) !=-1 ) {
total += read;
}
if( total < bytesAvailable ){
Log.e(TAG, "Read from input stream failed")
return;
}
//or try inputStream.readAllBytes() if using Java 9
inputStream.close();
ByteBuffer bb = ByteBuffer.wrap(buffer);
Movie m2 = MovieCreator.build(new ByteBufferByteChannel(bb),
new InMemRandomAccessSourceImpl(bb), "inmem");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
But I would say, there looks to be somewhat of a conflict between what you want to achieve and the approach the parser takes. It is depending on local files to avoid large memory overheads, and random access to bytes can only be done if the entire set of data is available, which differs from a streaming approach.
It will require buffering at least the amount of data required for your clip in one go before the parser is given the buffer. That might be workable for you if you are looking to grab short sections and the buffering is not too cumbersome. You may be subject to IO exceptions and the like if the read from the InputStream has issues, especially if it is remote content, whereas you really aren't expecting that with a file on a modern system.
There is also MemoryFile to consider which provides an ashmem backed file-like object. I think somehow that could be worked in.
Next a snipped shows how to open a MediaStore Uri with IsoFile from Mp4Parser. So, you can see how to get a FileChannel from a Uri.
public void test(#NonNull final Context context, #NonNull final Uri uri) throws IOException
{
ParcelFileDescriptor fileDescriptor = null;
try
{
final ContentResolver resolver = context.getContentResolver();
fileDescriptor = resolver.openFileDescriptor(uri, "rw");
if (fileDescriptor == null)
{
throw new IOException("Failed to open Uri.");
}
final FileDescriptor fd = fileDescriptor.getFileDescriptor();
final FileInputStream inputStream = new FileInputStream(fd);
final FileChannel fileChannel = inputStream.getChannel();
final DataSource channel = new FileDataSourceImpl(fileChannel);
final IsoFile isoFile = new IsoFile(channel);
... do what you need ....
}
finally
{
if (fileDescriptor != null)
{
fileDescriptor.close();
}
}
}
I am working on an xamarin app that has images stored in an S3 bucket. The querying works correctly in xamarin when using the correctly constructed Url:
https:// + BucketName + path + ".jpg?AWSAccessKeyId=keycode&Expires=expireNumber&Signature=signatureCode"
When using
Image.Source = urlAddress (as the above format)
The image is loaded fine
Part of the apps pages have custom renderers with Images that need to be rendered via url address. We are updating the images via url at each os level. The iOS is working correctly using the following code:
using (var url = new NSUrl(uri))
using (var data = NSData.FromUrl(url))
if (data != null)
return UIImage.LoadFromData(data);
Which successfully gets the image from Url and updates it. However I am having major issues having it work on Android. I have tried the following area:
making a basic android url and setting the imageView with the following code. Which has been explained to not work here https://forums.xamarin.com/discussion/4323/image-from-url-in-imageview
Android.Net.Uri url = Android.Net.Uri.Parse(url);
imageView.SetImageURI(url);
On that same link using WebClient was suggested by user 'rmacias' to download the data via the url and parse the bytes to an android Bitmap.
private Bitmap GetImageBitmapFromUrl(string url){
Bitmap imageBitmap = null;
using (var webClient = new WebClient())
{
var imageBytes = webClient.DownloadData(url);
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
}
}
return imageBitmap;}
This returns a 403 forbidden error. at the line var imageBytes = webClient.DownloadData(url)
However the same process is working in iOS, the string is already authenticated and I have set the authentication timeout for several minutes incase of slow load. I have also tiued the same url requesting method with the .Net.Http library.
It crashes at res = (HttpWebResponse)request.GetResponse(); with the same 403 Forbidden error.
I have tried multiple things with header authentications for the WebClient and Http client. It feels that its something specific about android requesting url data because the authentication in the url string works for the Xamarin images and in the ioS code.
I'm thinking there is something specific to android that I am missing? Help is much appreciated!
How about using HttpClient, which can leverage the platform specific HttpClientHandler's which Xamarin provides?
So something like:
// make sure to reuse your HttpClient instance, it is a shared resource
// using it in a using() and disposing it all the time, will leave
// sockets open and bog down the connection!
private static HttpClient _httpClient;
public async Task<byte[]> GetImageDataAsync(string url)
{
if (_httpClient == null)
{
// you could inject AndroidHttpClientHandler or NSUrlSessionHandler here...
_httpClient = new HttpClient();
// set headers etc...
}
var response = await _httpClient.GetAsync(url).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
return null;
var result = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
return result;
}
Then you can use this platform agnostically like:
var data = await GetImageDataAsync(url);
imageBitmap = BitmapFactory.DecodeByteArray(data, 0, data.Length);
on iOS
var data = await GetImageDataAsync(url);
var imageData = NSData.FromArray(data);
imageBitmap = UIImage.LoadFromData(imageData);
There are also nice libraries, such as FFImageLoading, which support this out of the box, with effects, loading of images in TableViews etc., which you can consider as an alternative.
I am setting a HttpCookie in my android application associated with a domain within the cookieManager. Then, when I stream HLS videos using exo player, I want this cookie to used for each of the .ts chunk requests.
This is the code, I use to store the cookie in the Application section of the project:
String url1 = "http://sdtest.vzvisp.com";
URI auri1 = null;
try {
auri1 = new URI(url1);
} catch (URISyntaxException e) {
e.printStackTrace();
}
AppGlobal.cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
AppGlobal.cookieManager.getCookieStore().add(auri1, new HttpCookie("JSESSIONID","aaaMp0uKke4gp"));
AppGlobal.cookieManager.getCookieStore().add(auri1,new HttpCookie("JSESSIONID1","aaaMp0uKke4gasasasp"));
AppGlobal.currentHandler = CookieHandler.getDefault();
if (AppGlobal.currentHandler != AppGlobal.cookieManager) {
CookieHandler.setDefault(AppGlobal.cookieManager);
}
This is an example of a chunk request sent :
http://sdtest.vzvisp.com:22779/AppConfig/SIT/fios/hls/fios/fios_hls_1m_00004.ts?vzSvc=fU2h73FMzhPgj8w0VNqYYsQ3lVZq8jjIWr6Xfrmraq4=&vispVzKey=54038752&vispIconFg=1&vispUsr=ZPq5BrbWoSQm1nsCNTPfBA==&vispAuthSid=CIAAASAIuwE&vispExpTime=1481081538&vispAuthKey=36958733&vispAuthSign=7.23.GMoCIpxmT_k123kT3P8iJxmCF-BWmeiAXQontU11hUI
But, when I inspect the packet, I do not see the cookie sent. Can someone kindly help me out ?
Thanks.
Try doing something like this:
AppGlobal.cookieManager.setCookie("http://sdtest.vzvisp.com","JSESSIONID=aaaMp0uKke4gp; Domain=.vzvisp.com; Path=/; Version=1");
Check this ticket to see how to set hls cookie correctly:
Hope this help
I've been working on an Android application that shows live streaming video via RTSP.
Assuming I have a well-functioning RTSP server that passes h264 packets, and to view the stream we should connect to rtsp://1.2.3.4:5555/stream
So I tried to use the native MediaPlayer\VideoView, but no luck (the video was stuck after 2-3 seconds of playback, so I loaded mrmaffen's vlc-android-sdk (can be found here) and used the following code:
ArrayList<String> options = new ArrayList<String>();
options.add("--no-drop-late-frames");
options.add("--no-skip-frames");
options.add("-vvv");
videoVlc = new LibVLC(options);
newVideoMediaPlayer = new org.videolan.libvlc.MediaPlayer(videoVlc);
final IVLCVout vOut = newVideoMediaPlayer.getVLCVout();
vOut.addCallback(this);
vOut.setVideoView(videoView); //videoView is a pre-defined view which is part of the layout
vOut.attachViews();
newVideoMediaPlayer.setEventListener(this);
Media videoMedia = new Media (videoVlc, Uri.parse(mVideoPath));
newVideoMediaPlayer.setMedia(videoMedia);
newVideoMediaPlayer.play();
The problem is that I see a blank screen.
Keep in mind that when I put a RTSP link with audio stream only, it works fine.
Is someone familliar with this sdk and have an idea about this issue?
Thanks in advance
Try adding this option:
--rtsp-tcp
I play rtsp streaming with following code
try {
Uri rtspUri=Uri.parse("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov");
final MediaWrapper mw = new MediaWrapper(rtspUri);
mw.removeFlags(MediaWrapper.MEDIA_FORCE_AUDIO);
mw.addFlags(MediaWrapper.MEDIA_VIDEO);
MediaWrapperListPlayer.getInstance().getMediaList().add(mw);
VLCInstance.getMainMediaPlayer().setEventListener(this);
VLCInstance.get().setOnHardwareAccelerationError(this);
final IVLCVout vlcVout = VLCInstance.getMainMediaPlayer().getVLCVout();
vlcVout.addCallback(this);
vlcVout.setVideoView(mSurfaceView);
vlcVout.attachViews();
final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
final String aout = VLCOptions.getAout(pref);
VLCInstance.getMainMediaPlayer().setAudioOutput(aout);
MediaWrapperListPlayer.getInstance().playIndex(this, 0);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
When you get playing event, you need enable video track.
private void onPlaying() {
stopLoadingAnimation();
VLCInstance.getMainMediaPlayer().setVideoTrackEnabled(true);
}
This may be helpful for you
google-api-java-client is a good web service api client on Android, however I mean a problem and spend lots of time on it.
I want to upload a file via POST method, so I study the google-drive API source code, but it uses the media uploader to upload file to google drive.
so how to upload a file via POST method in google-api-java-client?
for example Imgur upload API method has two require parameter.
suppose a upload file API written in google-api-java-client like this, but i have no idea how should I fill in the 'image' field which marked ???
public class ImgurImage extends GenericJson {
#com.google.api.client.util.Key("key")
private String mKey;
#com.google.api.client.util.Key("image")
private ??? mImage
}
public class Upload extends JsonHttpRequest {
private static final String REST_PATH = "/2/upload.json"
private Upload(ImgurImage content) {
super(ImgurClient.this, HttpMethod.POST, REST_PATH, content);
}
public void execute() throws IOException {
executeUnparsed();
}
}
I recommend taking a look at our Drive API sample at http://samples.google-api-java-client.googlecode.com/hg/drive-cmdline-sample/instructions.html
Here's a code snippet:
private static final java.io.File UPLOAD_FILE = new java.io.File(UPLOAD_FILE_PATH);
/** Uploads a file using either resumable or direct media upload. */
private static File uploadFile(boolean useDirectUpload) throws IOException {
File fileMetadata = new File();
fileMetadata.setTitle(UPLOAD_FILE.getName());
InputStreamContent mediaContent = new InputStreamContent("image/jpeg", new BufferedInputStream(
new FileInputStream(UPLOAD_FILE)));
mediaContent.setLength(UPLOAD_FILE.length());
Drive.Files.Insert insert = drive.files().insert(fileMetadata, mediaContent);
MediaHttpUploader uploader = insert.getMediaHttpUploader();
uploader.setDirectUploadEnabled(useDirectUpload);
uploader.setProgressListener(new FileUploadProgressListener());
return insert.execute();
}
I use httpClient and I use FileBody to send a file to a server, as part of a MultiPartEntity. This works for Picasa, Flickr, etc.
it looks like you adopted a sample that was uploading text (json in content, json in the REST URL endpoint) and used it to attempt image upload ( upload(ImgurImage) ).
So, look at the source for the 'GenericJson.upload(ImgurImage)'...
See what this method does when it sets the 'content' field prior to calling 'exec' on the POST.
Understand that these frameworks will allow either strings and text in content or allow bitmaps ( bytes ) for images and audio type uploads.
Look for a handler that sets the 'content' and understand how it handles both bytes and strings for the json sample.
that should help you use the framework you have chosen.