Android 播放SMB共享视频
表面上看MediaPlayer只能播放本地和http协议视频。没有直接支持smb://协议。那还能播放smb视频呢?也可以的!
MediaPlayer有一个方法叫:setDataSource(MediaDataSource)。
/**
* Sets the data source (MediaDataSource) to use.
*
* @param dataSource the MediaDataSource for the media you want to play
* @throws IllegalStateException if it is called in an invalid state
* @throws IllegalArgumentException if dataSource is not a valid MediaDataSource
*/
public void setDataSource(MediaDataSource dataSource)
throws IllegalArgumentException, IllegalStateException {
_setDataSource(dataSource);
}
我们只要实现MediaDataSource这个接口就可以了。
/**
* For supplying media data to the framework. Implement this if your app has
* special requirements for the way media data is obtained.
*
* <p class="note">Methods of this interface may be called on multiple different
* threads. There will be a thread synchronization point between each call to ensure that
* modifications to the state of your MediaDataSource are visible to future calls. This means
* you don't need to do your own synchronization unless you're modifying the
* MediaDataSource from another thread while it's being used by the framework.</p>
*/
public abstract class MediaDataSource implements Closeable {
/**
* Called to request data from the given position.
*
* Implementations should fill {@code buffer} with up to {@code size}
* bytes of data, and return the number of valid bytes in the buffer.
*
* Return {@code 0} if size is zero (thus no bytes are read).
*
* Return {@code -1} to indicate that end of stream is reached.
*
* @param position the position in the data source to read from.
* @param buffer the buffer to read the data into.
* @param offset the offset within buffer to read the data into.
* @param size the number of bytes to read.
* @throws IOException on fatal errors.
* @return the number of bytes read, or -1 if end of stream is reached.
*/
public abstract int readAt(long position, byte[] buffer, int offset, int size)
throws IOException;
/**
* Called to get the size of the data source.
*
* @throws IOException on fatal errors
* @return the size of data source in bytes, or -1 if the size is unknown.
*/
public abstract long getSize() throws IOException;
}
下面是我实现的代码:
public class CustomMediaDataSource extends MediaDataSource {
private static Logger Log4j = Logger.getLogger(CustomMediaDataSource.class);
private SmbRandomAccessFile mFile; // must not Main UI thread.
private long mFileSize;
public CustomMediaDataSource(SmbRandomAccessFile smbFile, long size) throws SmbException {
this.mFile = smbFile;
mFileSize = size;
}
@Override
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
if (mFile.getFilePointer() != position)
mFile.seek(position);
if (size <= 0)
return 0;
Log4j.info("CustomMediaDataSource, readAt, position:" + position);
return mFile.read(buffer, 0, size);
}
@Override
public long getSize() throws IOException {
return mFileSize;
}
@Override
public void close() throws IOException {
mFileSize = 0;
if (mFile != null) {
mFile.close();
mFile = null;
}
}
}
这里有一个小插曲, 导致花费了大半天时间。视频播放不能用InputStream接口,要用RandomAccess接口。因为视频播放的数据不是按顺序取的。不然就会报错:java.io.IOException: Prepare failed.: status=0x1。
如果你是其他播放器的, 同样也会有这样的接口提供给你。比如ljkPlayer等。
这个主题值得写一下,网上基本没有讲到。