做音乐播放器,有时候会用到系统自带的mediaplayer播放器,这个播放器底层是在linux上面,封装了一些api供使用者调用,由于网络HTTP请求歌曲流这一块的都已经被封装了,所以要想实现歌曲边下载同时还能缓存到我们的文件夹中,需要做的就是从请求过程中间再开辟一个中间代理,通过代理去处理之间的输入输出流,同时在代理中实现本文的功能,这样就能实现我们想要的边下边播了。

本文的采用的就是利用创建本地socket代理,通过代理mediaplayer发送的请求,之后由代理去发送真实的请求,获取真实地址返回数据,然后再由代理写回给mediaplayer,同时写给本地去缓存。

小坑:

1、只采用一个socket代理+ 一个http的模式,歌曲可以播放正常,但是mediaplayer的seekTo方法失效,原因:分析mediaplayer的请求抓包,少了一些请求数据,导致mediaPlayer的duration一直为0。

2、碰到服务端返回采用了ngnix的redirect 也就是302跳转,导致本地文件会写的特别小,因为mediaplayer会根据302跳转标识去解析redirect的地址然后重新发送绕过代理的新请求。

所以本文最后采用的是一个本地socket,专门负责代理mediaplayer的请求和回写,通过捕获mediaplayer请求,替换掉http中的请求地址,然后通过另一个socket代理去处理发送至真实请求地址,根据请求回来的返回流,分析是否是歌曲,是歌曲流返回给mediaplayer,不是歌曲流,解析出里面的真实地址,再次发送远程socket,继续迭代分析,最终获取歌曲流并解析。也就是说1个socket与mediaplayer打交道,一个与网络打交道。

逻辑上就是上面了,接下来就是实现之(准备了一份demo,因为和太多demo放一起了,此次暂不放出,之后会统一放入github上)

主要代码:

初始化本地代理:

  public void init() {isThreadRun = true;try {localSocket = new ServerSocket(LOCAL_PORT, 0, InetAddress.getByName(LOCAL_IP_ADDRESS));//监听本地端口和IPlocalSocket.setSoTimeout(TIME_OUT);} catch (Exception e) {MLog.error(this, "Can`t create a local serverSocket to listener local port");}}

重点代理类在线程中运行:

  @Overridepublic void run() {while (isThreadRun) {try {Socket client = localSocket.accept();//本地socket代理监听本地if (client == null) {continue;}newSocket = client;MLog.debug(this, "socket,get it!");MediaPlayerRequestInfo info = readRequest(client);//获取之前的请求并转换成新的请求Socket remoteSocket = sendRemoteRequest(info);//将新的数据请求协议发送出去if (remoteSocket == null) {continue;}processRequest(remoteSocket, client, info);//代理获取信息后开始中转} catch (IOException e) {}}}

主要处理数据类:

该类用于读取之前的协议,然后替换成真实的请求地址,然后给后续发送

private MediaPlayerRequestInfo readRequest(Socket client) {//从本地代理中读取请求信息,然后加工替换为新的请求流,用于远程socket转发int readBytes;MediaPlayerRequestInfo info = new MediaPlayerRequestInfo();//自己封装的类,主要放入实际上要请求的地址以及歌曲的相关状态值try {InputStream localInputStream = client.getInputStream();String infactRequest = "";while ((readBytes = localInputStream.read(buffer)) != -1) {String str = new String(buffer);infactRequest += str;if (infactRequest.contains("GET") && infactRequest.contains("\r\n\r\n")) {//找到请求头中的get和\r\ninfactRequest = infactRequest.replaceAll(LOCAL_IP_ADDRESS + ":" + LOCAL_PORT, remoteHost);//替换为真实的请求地址info.realRequest = infactRequest;//----以下代码用于本地文件创建,不属于此次所述内容,可忽略,部分已删除File file = new File(DownloadMusicFileUtils.getMusicCacheDownloadPath() + File.separator +********));if (!file.exists()) {file.mkdirs();}//----------------------------if (infactRequest.contains("Range")) {//如果用户拖动了进度条,删除本地文件,因为拖动了滚动条还有Range则表示本地歌曲还未缓存完,不保存MLog.debug(this, infactRequest);MLog.debug(this, "this is range socket ,so do not need to write on local storage, and need to delete the file from local");deleteFile(info.fileName);info.shouldWrite = false;//提示该应用这次的请求都不用去管了,毕竟是range的,不是完整歌曲}break;}}} catch (IOException e) {MLog.debug(this, "this request info is error");return info;}return info;}

该类用于对转发后的socket读取到的数据,同时写给mediaplayer和本地

 private void processRequest(Socket remoteSocket, Socket client, MediaPlayerRequestInfo info) throws IllegalStateException, IOException {InputStream realInputStream = remoteSocket.getInputStream();//获取真实返回数据if (realInputStream == null) {return;}OutputStream localOutputStream = client.getOutputStream();//向本地播放代理写FileOutputStream cacheOut = null;//写本地文件if (info.shouldWrite) {File file = new File(DownloadMusicFileUtils.getMusicCacheDownloadPath() + File.separator + info.fileName);cacheOut = new FileOutputStream(file);}try {int readBytes;while (isThreadRun && (readBytes = realInputStream.read(buffer, 0, buffer.length)) != -1) {if (newSocket != client) {throw new Exception();}if (readBytes < 900) {//redirect 302 or last bytesString redirectStr = new String(buffer);if (redirectStr.contains("302") && redirectStr.contains("Location:")) {//部分代码删除了,此处可以省略,该处主要是判断http302的时候跳转就需要重新组装请求,发送,把之前的socket关闭,系统会在写的时候发现,然后抛异常,在异常中删除之前创建的文件Socket newRemoteSocket = sendRemoteRequest(info);//发送新请求if (remoteSocket == null) {return;}processRequest(newRemoteSocket, client, info);//调自己return;}}localOutputStream.write(buffer, 0, readBytes);try {if (info.shouldWrite) {cacheOut.write(buffer, 0, readBytes);}}catch(Exception e){info.shouldWrite = false;//文件写不进去的情况}}} catch (Exception e) {MLog.debug(this, "the socket is be closed,it means download is not finish and now should delete download file" + e.getMessage());deleteFile(info.fileName);info.shouldWrite = false;} finally {MLog.debug(this, "finish");realInputStream.close();localOutputStream.close();if (cacheOut != null) {cacheOut.close();}client.close();remoteSocket.close();}}

模拟mediaplayer发送协议

 private Socket sendRemoteRequest(MediaPlayerRequestInfo info) {Socket remoteSocket = null;try {remoteSocket = new Socket();remoteSocket.connect(new InetSocketAddress(remoteHost, HTTP_PORT));remoteSocket.getOutputStream().write(info.realRequest.getBytes());remoteSocket.getOutputStream().flush();} catch (Exception e) {return remoteSocket;}return remoteSocket;}

入口就是把datasource的请求url地址改成localIp和port就可以了,记得要把真实的请求地址也给过去,否则后续在代理中给远程socket使用的替换操作无法执行

mediaplayer.setDataSource(context, url)

重点的类应该就这些了,什么本地文件缓存管理,文件折半删除啊什么的就不放在一起讨论了

转载请注明:iamwsbear@gmail.com

///----------------------------博客空了好久了,之间写了好多demo,要放入博客,需要整理整理,以后会把代码统一放入github上------------------------

android mediaplayer 实现歌曲边播放边下载相关推荐

  1. Android MediaPlayer 音频倍速播放,调整播放速度

    本文链接: Android MediaPlayer 倍速播放,调整播放速度 现在市面上的很多音视频App都有倍速播放的功能,例如把播放速度调整为0.5.1.5.2倍等等. 从Android API 2 ...

  2. Android MediaPlayer 及 NativePlayer 之播放格式对比

    对于原生MediaPlayer 的支持格式测试: 对于原生 NativeMedia 的支持格式测试: 这个支持就比较失望了,目前测试的手机只支持 H.264视频及AAC音频,其它的格式都不支持.使用t ...

  3. Android MediaPlayer使用之网络访问异常

    Android MediaPlayer使用之网络访问异常 1.问题描述 2019-04-02 21:26:15.552 22138-22138/com.uyeh.mediaplayertest D/M ...

  4. Android MediaPlayer 音乐播放器扫描 本地音乐、上一曲、下一曲切歌、播放本地音乐

    Android MediaPlayer 本地音乐播放器 运行截图 项目请在真机(自己的手机)上测试运行,因为我不喜欢用虚拟机. 为了不浪费您的时间,先看一下运行的效果图, 一进去先进行音乐扫描,然后列 ...

  5. android studio 60音乐播放器 下载音乐

    activity_main.xml 111111111111111111 <?xml version="1.0" encoding="utf-8"?> ...

  6. Android音乐播放器制作(二 )点击歌曲实现播放

    上次我们实现了把手机里的音频扫描到,然后放在list集合里面,用ListView展示在手机界面上,如果没有看过的可以去看看本人的博客:Android音乐播放器制作(一)扫描本地音乐显示在手机上 这次是 ...

  7. 我的Android进阶之旅------Android MediaPlayer播放网络音频的实例--网络mp3播放器

    上一篇写了个简单的MP3播放器 ,这次写一个可以播放网络音频资源的播放器 本实例可以实现音乐播放器除了来电的时候会暂停播放,通话结束后恢复播放外,打开其他的Activity都可以继续播放音乐,享受一边 ...

  8. 我的Android进阶之旅------Android MediaPlayer播放mp3的实例--简易mp3播放器

    大家好我们今天研究的是Android中很重要也最为复杂的媒体播放器---MediaPlayer. Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上 ...

  9. android mediaplayer 权限,Android — 使用MediaPlayer进行音频播放

    上一篇说到音频录制的时候我就知道肯定会有音频播放这个功能,果不其然后续播放的需求又给我加上了,因为之前是AMR格式,所以这里使用MediaPlayer进行播放,据ios说播放amr格式的音频还挺麻烦的 ...

最新文章

  1. 率清华团队研发“天机芯”登《Nature》封面,他说类脑计算是发展人工通用智能的基石...
  2. git服务器安装位置,Linux服务器安装gitlabe-runner,并部署包到指定目录,还有踩的一些坑~~...
  3. python手机版编程-手机编python
  4. MySQL binlog和redo/undo的概念
  5. 【深入浅出jQuery】源码浅析--整体架构(转)
  6. Java时间和日期指南
  7. 如何关闭hibernate产生的大量日志
  8. jsp mysql超市管理系统_jsp超市管理系统 基于jsp+servlet+MySQL的超市管理系统(带数据库) - 下载 - 搜珍网...
  9. 【4】SCN-Feign远程调用
  10. 关于计算机硬盘属性对话框中,计算机文化基础练习题(2).docx
  11. 深入浅出mysql 出版社_深入浅出mysql
  12. halcon相机标定助手_Halcon教程之单相机标定
  13. 电子科技大学计算机学生水平,近三年的学生评价结果-电子科技大学.DOC
  14. 快速学习OSG(2)——光照
  15. 计算机时间怎么改24小时模式,怎么修改电脑的时间为24小时制啊?
  16. PCB钻孔输出的一个简单动作,却带来工厂的命运转折
  17. PointGet的一生
  18. php 快速导出百万级数据到 csv 或者 excel 文件
  19. 【转】英语中的从属连词,28个,一篇搞定(句子结构2)
  20. VMWare------启动虚拟机时出现“start booting fron CD... Error loading image:DFEAULT.EZB”提示...

热门文章

  1. 门户网站建设有哪些类型?
  2. org.quartz
  3. STM32 FLASH的写入与读取
  4. 【OpenAI-maddpg】训练时遇到的一些错误
  5. 基于Java实现在线翻译【100010578】
  6. 【电子技术综合设计】数字钟(包含计数模块、12/24进制切换模块以及闹钟模块)
  7. 用批处理创建OU和域用户
  8. 典型环节matlab仿真,实验一典型环节的matlab仿真
  9. 【电气专业知识问答】问:电网调度部门对各主要发电厂涉网部分电气设备的监控内容是什么?如何实现?
  10. UbuntuLinux 16.04系统升级Docker CE