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
Related
Here's my problem: I'm writing a laravel backend which have to serve an mp3 file that had to be reproduced by using the android standard media player.
For the laravel backend I need to use JWT to handle authentication so on every request headers I have to set the "Authorization" field to "Bearer {token}" .The laravel route is "/songs/{id}" and is handled in this way:
public function getSong(Song $song) {
$file = new File(storage_path()."/songs/".$song->path.".mp3");
$headers = array();
$headers['Content-Type'] = 'audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3';
$headers['Content-Length'] = $file->getSize();
$headers['Content-Transfer-Encoding'] = 'binary';
$headers['Accept-Range'] = 'bytes';
$headers['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0';
$headers['Connection'] = 'Keep-Alive';
$headers['Content-Disposition'] = 'attachment; filename="'.$song->path.'.mp3"';
$user = \Auth::user();
if($user->activated_at) {
return Response::download($file, $song->path, $headers);
}
\App::abort(400);
}
On the android side I'm using the MediaPlayer to stream the mp3 file in this way:
media_player = new MediaPlayer();
try {
media_player.setAudioStreamType(AudioManager.STREAM_MUSIC);
String token = getSharedPreferences("p_shared", MODE_PRIVATE).getString("token", null);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
media_player.setDataSource(
getApplicationContext(),
Uri.parse(ConnectionHelper.SERVER + "/songs/" + song.getId()),
headers
);
} catch (IOException e) {
finish();
Toast.makeText(
Round.this,
"Some error occurred. Retry in some minutes.",
Toast.LENGTH_SHORT
).show();
}
media_player.setOnCompletionListener(this);
media_player.setOnErrorListener(this);
media_player.setOnPreparedListener(this);
But every time I execute the code I get extra code -1005 on the error listener that means ERROR_CONNECTION_LOST.
The problem: Response::download(...) doesn't produce a stream, so I can't serve my .mp3 file.
The solution:
As Symfony HttpFoundation doc. says in the serving file paragraph:
"if you are serving a static file, you can use a BinaryFileResponse"
The .mp3 files I need to serve are statics in the server and stored in "/storage/songs/" so I decided to use the BinaryFileResponse, and the method for serving .mp3 became:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
[...]
public function getSong(Song $song) {
$path = storage_path().DIRECTORY_SEPARATOR."songs".DIRECTORY_SEPARATOR.$song->path.".mp3");
$user = \Auth::user();
if($user->activated_at) {
$response = new BinaryFileResponse($path);
BinaryFileResponse::trustXSendfileTypeHeader();
return $response;
}
\App::abort(400);
}
The BinaryFileResponse automatically handle the requests and allow you to serve the file entirely (by making just one request with Http 200 code) or splitted for slower connection (more requests with Http 206 code and one final request with 200 code).
If you have the mod_xsendfile you can use (to make streaming faster) by adding:
BinaryFileResponse::trustXSendfileTypeHeader();
The android code doesn't need to change in order to stream the file.
I have an ASP.NET C# MVC4 Web site that I have working wonderfully for the most part. However, when we tested on mobile, the cookies that I am using for authentication would not work. I set the Auth cookie in my controller action but when trying to access them on the next call they are not there. Once again this is ONLY A PROBLEM ON MOBILE. Works fine in desktop versions of IE, Chrome and Firefox. Does not work with Chrome on Android.
Code to write cookie (in controller action):
//Set information into object that can be read out of the cookie later
FormsAuthModel UserDataObj = new FormsAuthModel
{
UserID = dmUser.ID,
PasswordChange = dmUser.PasswordChange
};
string UserData = Convert.ToBase64String(clsShared.Serialize(UserDataObj));
//Create the ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, dmUser.UserName, DateTime.Now, DateTime.Now.AddDays(1), false, UserData, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
System.Web.HttpContext.Current.Response.Cookies.Add(cookie);
Code to read cookie (in Global.asax.cs - Application_PostAuthenticateRequest):
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
try
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
UserDataObj = (FormsAuthModel)clsShared.Deserialize(Convert.FromBase64String(authTicket.UserData), typeof(FormsAuthModel));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
//WriteEvent(string.Format("Error deserializing auth ticket - {0}", ex.Message), EventLogEntryType.Error);
}
}
The AuthCookie is always null on the subsequent requests. What the user sees is a login screen, they fill it out and they get redirected right back to the login screen.
I could not find anything in my searches that helped explain why all the mobile requests (my phone, my tablet and other users' phones) would act differently than the desktop browsers.
Any help would be greatly appreciated.
Thanks!!
OK I found a solution although I am not sure why. I changed the cookie creation code as follows and it worked.
//Set information into object that can be read out of the cookie later
FormsAuthModel UserDataObj = new FormsAuthModel
{
UserID = dmUser.ID,
PasswordChange = dmUser.PasswordChange
};
string UserData = Convert.ToBase64String(clsShared.Serialize(UserDataObj));
//Create the ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, dmUser.UserName, DateTime.Now, DateTime.Now.AddDays(1), false, UserData, FormsAuthentication.FormsCookiePath);
// Encrypt the ticket
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie - FIX IS HERE!!!
Response.Cookies[FormsAuthentication.FormsCookieName].Value = encTicket;
//HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
//Response.Cookies.Add(cookie);
Notice that the only change is in adding the cookie by setting the value directly instead of creating a cookie object and adding that to the collection.
i.e. - Response.Cookies["Name"] = Value;
I got the idea from this MS article: https://msdn.microsoft.com/en-us/library/ms178194.aspx.
So does anyone know why this would make a difference? I have used the cookie instance method several times before and never had this problem.
I'm creating a fairly simple Android app for work that will plug a URI into the system video player so that users can easily watch the RTSP stream that we are sending from our Wowza server.
In general, I basically just have a play button that calls:
Uri myURI = Uri.parse("rtsp://ipaddress:1935/path/myStream");
Intent intent = new Intent(Intent.ACTION_VIEW, myURI);
startActivity(intent);
but we don't stream from this URI 24/7 and I would like to change the behavior during onCreate so that it will validate the URI before drawing the button, giving me the opportunity to give the user visual feedback that the stream isn't live and save us unneeded support emails.
I've looked at the Uri and URI classes to see if there was a method that I could call to check the URI but nothing seems to really exist. Other URI related questions seem to refer to local files so the dev can just check a file, but I don't think that would work for a live stream. Any advice would be greatly appreciated!
Basically you want to open a plain client socket connection to the RTSP server hostname/port and then post a RTSP OPTIONS request.
If the server responds with a "RTSP/1.0 200 OK", it means the streaming service is up, otherwise it's down.
I wrote a quick and dirty sample RTSP check and tested it out. Feel free to adapt it to your needs:
Update handling UI operations
In your activity class, create these fields:
private static final int MESSAGE_RTSP_OK = 1;
private static final int MESSAGE_RTSP_ERROR = -1;
private Handler handler;
in your onCreate method, add:
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_RTSP_OK:
//show_player();
// implement ui operation here
break;
case MESSAGE_RTSP_ERROR:
//show_error();
break;
}
}
};
Then run this sample code:
Updated rtsp check code
new Thread() {
public void run() {
try {
Socket client = new Socket("a1352.l1857053128.c18570.g.lq.akamaistream.net", 554);
OutputStream os = client.getOutputStream();
os.write("OPTIONS * RTSP/1.0\n".getBytes());
os.write("CSeq: 1\n\n".getBytes());
os.flush();
//NOTE: it's very important to end any rtsp request with \n\n (two new lines). The server will acknowledge that the request ends there and it's time to send the response back.
BufferedReader br =
new BufferedReader(
new InputStreamReader(
new BufferedInputStream(client.getInputStream())));
StringBuilder sb = new StringBuilder();
String responseLine = null;
while (null != (responseLine = br.readLine()))
sb.append(responseLine);
String rtspResponse = sb.toString();
if(rtspResponse.startsWith("RTSP/1.0 200 OK")){
// RTSP SERVER IS UP!!
handler.obtainMessage(MESSAGE_RTSP_OK).sendToTarget();
} else {
// SOMETHING'S WRONG
handler.obtainMessage(MESSAGE_RTSP_ERROR).sendToTarget();
}
Log.d("RTSP reply" , rtspResponse);
client.close();
} catch (IOException e) {
// NETWORK ERROR such as Timeout
e.printStackTrace();
handler.obtainMessage(MESSAGE_RTSP_ERROR).sendToTarget();
}
}
}.start();`
For this test I picked the public NASA TV rtsp server: rtsp://a1352.l1857053128.c18570.g.lq.akamaistream.net:554
The code sends the following rtsp request:
OPTIONS * RTSP/1.0
CSeq: 1
And receives the following response:
RTSP/1.0 200 OK
Server: QTSS-Akamai/6.0.3 (Build/526.3; Platform/Linux; Release/Darwin; )
Cseq: 1
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD
If you are looking for more complex checks, feel free to dive into the rtsp protocol documentation and enhance the code per your own needs: https://www.ietf.org/rfc/rfc2326.txt
Please let me know if more information is needed.
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
I'm having an issue with a Multipart-form-data POST request using the Jersey Client API on Android. I've been following various examples on the web and they are all fairly similar in regards to the implementation.
Client client = createClientInstance();
WebResource r = client.resource(BASEURL).path("DataUpload");
ClientResponse post;
try {
FormDataMultiPart multiPart = new FormDataMultiPart();
multiPart.field("account", account);
multiPart.field("checksum", checksum);
multiPart.bodyPart(new FileDataBodyPart("file", file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
post = r.type(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.TEXT_PLAIN)
.post(ClientResponse.class, multiPart);
} catch (ClientHandlerException e) {
Log.e(TAG, e.getLocalizedMessage());
} finally {
client.destroy();
}
When I execute this code on my device, I am presented with an exception:
javax.ws.rs.WebApplicationException: java.lang.IllegalArgumentException: No MessageBodyWriter for body part of type 'java.io.File' and media type 'application/octet-stream'
I thought Jersey was supposed to handle File objects without any extra configuration. Removing the bodypart line will allow Jersey to make the request but that eliminates the point of this.
I have these libraries on my buildpath (which were pulled in with Maven):
jersey-client-1.14
jersey-core-1.14
jersey-multipart-1.14
mimepull-1-6
I can suggest two things to try:
remove the MIME type from the FileDataBodyPart construction to see if Jersey can find a mime type it is happy to default to :
multiPart.bodyPart(new FileDataBodyPart("file", file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
tell your client config about the multipart body writer (presumably inside your createClientInstance() method) :
com.sun.jersey.api.client.config.ClientConfig config = new com.sun.jersey.api.client.config.DefaultClientConfig();
config.getClasses().add(MultiPartWriter.class);
client = Client.create(config);
Hope that helps.