📜 ⬆️ ⬇️

Android MediaPlayer. Expanding capabilities with a proxy


Implementing a proxy for the standard MediaPlayer component carries many more advantages than it might seem at first glance. This article describes in detail how this all works and about the prospects for the development of such 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 experienced ExoPlayer. He has no banal flv support. 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 more or less a suitable solution, but after talking with its developer Crossle, I learned that there was no waiting for help from him. He does not fix bugs and in general almost no one is involved in this project. Since there were a lot of bugs, I returned to the standard player. It seemed to me the fastest and the most bug-free. Of course there are not so many functions in it, as in ExoPlayer or Vitamio. Therefore, I began to learn how to add them yourself.
I did not use VideoView and made a custom class player. In it, I added a lot of my Event-s for all occasions. The player is based on textureView. I was looking for a way to make the player play in the background and during the re-creation of the Activity. With SurfaceView this did not work ... TextureView - works fine. Spin, cool ... and the music plays. I did not find the reasons for transferring the player to the service. And why, when and so everything works well. As soon as the reason to appear - the service will be created.

The tasks were two simple ones - 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 you first had 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 is not so good with the cache), and then you get a combine that plays all formats and supports all protocols), but I do not recommend it. Since the variety of formats is not only good, but also evil. The player needs support for the main formats and some minor ones, not all in a row. This will lead to purity and quality improvement of the material for users. In addition, support for formats should be at the system level, embedded codecs, and not at the player level. Vitamio - has many bugs with out of sync audio and video, poor performance and increases the size of the apk application by 10-15 megabytes.
')
Key features:

Determine access rights:
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> 

Wake_Lock - so that the player would not stop playing when the device is in sleep mode.
WRITE_EXTERNAL_STORAGE - to write the cache to the external storage device.

MainActivity.java
 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 proxy variable class. And set the parameters:
proxy.setPaths (folder where to save the cache, the link to the file on the Internet itself, cache size, number of files in the cache, context, delete files that have not been downloaded ?, login FTP, FTP password, FTP compatibility mode?);
Get the proxy path for the player String proxyUrl = proxy.getLocalURL ();
Start the player textureView.setVideoPath (proxyUrl);

Proxy starts from the line proxy = new HttpGetProxy () ;. There is a command to stop proxy.stopProxy (). This command closes the local socket that is waiting for requests from the player and then ends the Thread proxy. Roughly, but not yet found a softer way out.

It is not difficult to guess how to make a proxy only for reading. Whatever is written to the memory card. This is not yet implemented, but you can put the values ​​BUFFER_SIZE, NUM_FILES and all files will be deleted by zeros.

Algorithm of the player:

Having written the first version of the proxy, I ran into the features of the player. I do not know what it is connected with, but problems arise with large flac files. The player is not exactly designed for this kind of files and therefore has more bugs in the algorithm of their playback.

Typical playback starts with two requests. Why not one for me is still a mystery. Those. The player requests the entire file, reads the first 50kb-100kb and resets the connection. It requests the entire file a second time and starts reading the file again from the beginning. Further requests become with indication of the initial byte.
I tried to make two thread 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. And the first connection, he breaks almost immediately.
So this is not all the problems. For some strange features after the first portion of the file, the player begins to read the end of the file. Selects a point before the end and reads to the end. For example, the last 200kb. Apparently looking for some data that is not found at the beginning or checks something. Then continues reading first.
Problems with flac files are a slow proxy speed during rewind and a bad algorithm. When rewinding flac player looks for a suitable beginning of the stream. Those. it can not start playing from any byte. Therefore makes 1-6 requests. Apparently at this moment some timeouts are used, so that the player works quickly, so I increased the speed of the sockets with a timeout of 1500ms ... Without a timeout, the sockets hang for a long time in inactivity and instead of 1-6 requests only 1-2 occur. Which reduces the likelihood that the player will find the beginning. And if the player does not find the beginning, it gives an error about the unsupported format. The timeout completely solves the problem of hanging the sockets, but does not solve the problem of the algorithm of the player. Since even without a proxy, when the flac files rewind, the player periodically gives an error “not supported format”.

Checked - the player makes the same number of requests, both with a proxy and without. Proxy requests are a bit slower. Approximately 50-200ms ... Due to various transformations and the opening / closing of sockets, which has been reduced to the maximum in recent versions.

Algorithm HTTP, FTP:

It is the procedure for closing sockets or threads that takes the most time. It is from these procedures and can be waived in some cases to increase performance.
Using the FTP RETR command, the file is retrieved. The ABOR command is necessary to correctly interrupt the file transfer. Yes, it is right and good. Only after all some FTP servers can work immediately taking a new command. Those. without the ABOR command, immediately REST, RETR and we already receive a new file from the byte specified in the REST. Sending an ABOR command takes about 100ms ... It degrades the comfort and speed of the proxy. For this, and created the "compatibility mode FTP". In this mode, the ABOR command is sent. No matter how I tried to automate the choice of the mode of operation, it did not work out. Therefore, made by hand. Without the ABOR command should work too. It will just be a new authorization, instead of interruption, and there may be a problem with the sessions. It is necessary to watch the FTP log - if authorization occurs almost every time, then it is better to enable the mode.

FTP server settings:

Especially there were not many tests. It should work in passive mode with open 21 ports 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 10. Session timeout 10 minutes. Connection timeout 1 minute.

Algorithm HTTP proxy:



The numbers indicate the sequence of actions.

For an FTP server, the sequence will be slightly different. In the proxy, there are more algorithms for switching to reading from a local file, if the Internet is not available or there is no response from the server. Mostly the algorithms are very complex. I tried my best to create short constructions. 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 on the basis of those that support data reception from a remote server and starting from a certain byte, you can make a proxy for the player and play multimedia.

File saving algorithm:

It needs to be improved, but even now it works perfectly. The point is simple - if you have listened to the entire file, it is renamed to the original extension and name. The check is carried out up to a byte at the moment when the file is finished reading. If you rename not the extension, but the file name, then the multimedia device scanner will understand that this is some kind of multimedia and will start scanning it. And if suddenly the file is broken, you will receive a 100% processor load before the device is rebooted. Perhaps in different android systems something is different, but I have on 4.4.2 so.
The problem now is that only what the player requests and reads is cached. The organization needs some kind of resume at the time when the player does not read anything, so as not to load the channel and stop it without delay for requesting the player. While it is not possible to catch the moment when the player does not read anything from the Internet.

GitHub:
github.com/master255/ImmortalPlayer
Working example:
play.google.com/store/apps/details?id=com.immortalplayer

Source: https://habr.com/ru/post/241858/


All Articles