Android MediaPlayer Expanding with proxies
Implementing a proxy for the standard MediaPlayer component has many more advantages than it might seem at first glance. This article details how this all works and the prospects for developing such a technology.
For a long time I was looking for the most successful implementation of a media player for android for my Media Library application. I tested ExoPlayer. It does not have banal support for flv. To which the developers said that there is no time to add support for this format. Next, I began to use Vitamio. And Vitamio seemed at first a less or less suitable solution, but after talking with its Crossle developer, I found out that I could not expect help from him. He does not fix bugs and in general almost no one is engaged in this project. Since there were a lot of bugs, I returned to the standard player. He seemed to me the fastest and most buggy. Of course, it does not have as many functions as in ExoPlayer or Vitamio. So I began to learn how to add them myself.
I did not use VideoView and made a custom player class. I added a lot of my Events to it for all occasions. The player is based on textureView. I was looking for a way to make the player play in the background and when recreating the Activity. This didn’t work with SurfaceView ... TextureView - works fine. Twirl, twist ... and the music plays. I did not find a reason to transfer the player to the service. And why, when and so everything works well. As soon as the reason appears, the service will be created.
There were two simple tasks - to create a cache for the player and make it play from FTP. This was asked by one user of my player. He wants to listen to music from his NAS, which supports the FTP protocol.
As a programmer, I understand that at first it was necessary to write a proxy and through this proxy organize the conversion of HTTP <> FTP requests and data transfer. Which I did in my ImmortalPlayer project .
In the future, this proxy can be used with VItamio (which doesn’t do so well with the cache) and then we get a complete combiner that plays all formats and supports all protocols)), but I do not recommend doing this. Since the variety of formats is not only good, but also evil. The player needs support for the main formats and some secondary, but not all, ones. This will lead to a cleaner and better quality material for users. In addition, support for formats should be at the system level, built-in codecs, and not at the player level. Vitamio - has many bugs with audio and video out of sync, poor performance and increases the size of the apk application by 10-15 megabytes.
Wake_Lock - so that the player does not stop playing when the device is in sleep mode.
WRITE_EXTERNAL_STORAGE - to write the cache to the external media of the device.
proxy = new HttpGetProxy(); proxy.setPaths("/ProxyBuffer", videoUrl, BUFFER_SIZE, NUM_FILES, getActivity().getApplicationContext(), false, ftplogin, ftppass, false); String proxyUrl = proxy.getLocalURL(); textureView.setVideoPath(proxyUrl);
Assign a new instance of the class to the proxy variable. And we set the parameters:
proxy.setPaths (the folder where to save the cache, the link to the file on the Internet, the size of the cache, the number of files in the cache, context, delete downloaded files ?, FTP login, FTP password, FTP compatibility mode?);
We get the proxy path for the player String proxyUrl = proxy.getLocalURL ();
We start the player textureView.setVideoPath (proxyUrl);
The proxy starts already from the line proxy = new HttpGetProxy () ;. There is a command to stop proxy.stopProxy (). This command closes the local socket, waiting for requests from the player, and then the Thread proxy ends. Roughly, but has not yet found a milder way out.
It's not hard to guess how to make read-only proxies. That would not be written to a memory card. This has not yet been implemented, but you can set the values BUFFER_SIZE, NUM_FILES to zero and all files will be deleted.
The algorithm of the player:
Having written the first version of the proxy, I ran into the player's features. I don’t know what this is connected with, but problems arise with large flac files. The player is not entirely designed for this kind of files and therefore still has bugs in the algorithm for playing them.
Typical playback starts with two queries. Why not one is still a mystery to me. Those. the player asks for the entire file, reads the first 50kb-100kb and resets the connection. Requests the entire file a second time and starts reading again from the beginning of the file. Further, the requests are indicated with the starting byte.
I tried to make two stream proxies and thought that he was trying to read in two streams, but the player stubbornly did not want to send two requests at the same time. Only consistently. Moreover, he breaks the first connection almost immediately.
So this is not all the problems. For some strange feature, after the first portion of the file, the player begins to read the end of the file. Selects a point in front of the end and reads to the end. For example, the last 200kb. Apparently he is looking for some data that he did not find at the beginning or is checking something. Then continues to read first.
Problems with flac files are a low proxy speed when rewinding and a poor algorithm. When rewinding flac, the player searches for a suitable start to the stream. Those. he cannot start playing from any byte. Therefore, it makes 1-6 queries. Apparently at this moment some timeouts are activated so that the player works quickly, so I increased the speed of sockets with a timeout of 1500ms ... Without a timeout, sockets hang for a long time in idle time and instead of 1-6 requests, only 1-2 are processed. Which reduces the likelihood that the player will find a start. And if the player does not find the beginning, then it gives an error about an unsupported format. The timeout completely solves the problem of hanging sockets, but does not solve the problem of the player’s algorithm. Since even without a proxy when rewinding flac files, the player periodically gives an error “unsupported format”.
Checked - the player makes the same number of requests, with or without a proxy. With proxies, requests run a little slower. Approximately 50-200ms ... Due to various conversions and opening / closing sockets, which has been minimized in recent versions.
The algorithm of HTTP, FTP:
It is the procedure for closing sockets or flows that takes the most time. It is from these procedures that you can refuse in some cases to increase performance.
Using the FTP RETR command, a file is received. The ABOR command is required to correctly interrupt file transfer. Yes, that’s right and good. Only after all, some FTP servers can work right away accepting a new command. Those. without the ABOR command - immediately REST, RETR and we already get a new file from the byte specified in REST. Sending the ABOR command takes about 100ms ... This degrades the comfort and speed of the proxy. For this, an “FTP compatibility mode” was created. In this mode, the ABOR command is sent. No matter how I tried to automate the choice of the operating mode, it did not work. Therefore, I did it manually. Without a command, ABOR should also work. There will simply be a new authorization, instead of interruption, and there may be a problem with the sessions. You need to watch the FTP log - if authorization occurs almost every time, it is better to enable the mode.
FTP server settings: There
were not many tests. It should exactly work in passive mode with 21 ports open for authorization and 1000 ports (maybe less) for data transfer. 1000 ports should be open on the firewall and specified in the ftp server. The number of simultaneous sessions is 10. The session timeout is 10 minutes. Connection timeout 1 minute.
The algorithm of the HTTP proxy: The
numbers indicate the sequence of actions.
For an FTP server, the sequence will be slightly different. In the proxy, there are also algorithms for switching to reading from a local file if the Internet is unavailable or there is no response from the server. In some places, the algorithms are very complex. I tried as much as possible to create short designs. I think something else can be reduced.
To implement the FTP protocol, I used the Apache Commons Net library. It supports several other protocols: FTPS, FTP over HTTP (experimental), NNTP, SMTP (S), POP3 (S), IMAP (S), Telnet, TFTP, Finger, Whois, rexec / rcmd / rlogin, Time (rdate ) and Daytime, Echo, Discard, NTP / SNTP.
I think based on those of them that support receiving data from a remote server and starting from a specific byte, you can make proxies for the player and play multimedia.
File saving algorithm:
It needs to be improved, but even now it works fine. The bottom line is simple - if you listened to the entire file, then it is renamed to the original extension and name. Validation is accurate to byte at the time the file is read. If you rename not the extension, but the file name, then the multimedia scanner of the device will understand that it is some kind of multimedia and will begin to scan it. And if suddenly the file turns out to be broken, you will get 100% processor utilization until the device reboots. Perhaps in different android systems something is different, but for me on 4.4.2 it is.
The problem now is that only what the player requests and reads is cached. We need to organize some kind of resuming at a time when the player is not reading anything, so as not to load the channel and stop it without delay to request the player. While it is not possible to catch the moment when the player does not read anything from the Internet.