- 浏览: 101942 次
- 性别:
- 来自: 北京
-
最新评论
-
wangshengyuan:
非常感谢博主,真是站在了巨人的肩膀上了
(五)用JAVA编写MP3解码器——解析文件信息 -
落枫飘飘:
楼主,你的播放器下载地址http://jmp123.sf.n ...
(附)用JAVA编写MP3解码器——GUI -
lfp001:
解码纯短块的增益因子时,解码得到12个频带的值。在逆量化纯短块 ...
(十)用JAVA编写MP3解码器——逆量化和重排序 -
lfp001:
逆量化混合块时:前8个频带是长块,用长块公式逆量化;后9个频带 ...
(八)用JAVA编写MP3解码器——解码增益因子 -
clearstarrysky:
短块:576个频谱值分为13个增益因子频带,但是在解码增益因子 ...
(十)用JAVA编写MP3解码器——逆量化和重排序
如果用HttpURLConnection类的方法打开连接,然后用InputStream类获得输入流,再用BufferedInputStream构造出带缓冲区的输入流,如果网速太慢的话,无论缓冲区设置多大,听起来都是断断续续的,达不到真正缓冲的目的。于是尝试编写代码实现用缓冲方式读取远程文件,以下贴出的代码是我写的MP3解码器的一部分。我是不怎么赞同使用多线程下载的,加之有的链接下载速度本身就比较快,所以在下载速度足够的情况下,就让下载线程退出,直到只剩下一个下载线程。多线程中的同步、HttpURLConnection的超时阻塞等因素都会使代码看起来异常复杂。
问题模型 数组buf[],N个“写”线程从文件顺序读入固定长度数据写入buf[];一个“读”线程按照文件内容顺序从缓冲区读取,一次读任意长度。一个写线程发生错误或文件读完,这个读线程和所有写线程退出。
针对上述问题,简要介绍一下实现多线程环形缓冲的方法。将缓冲区buf[]分为16块,每块32K,下载线程负责向缓冲区写数据,每次写一块;每次读小于32K的任意字节。线程同步:各个写线程互斥等待空闲块,用信号量机制分配空闲块;各写线程并发填写buf[];读线程和各写线程并发使用buf[]。
为突出主题略去了一些和本文无关的代码。
一、HttpReader类功能:HTTP协议从指定URL读取数据。
package instream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public final class HttpReader { public static final int MAX_RETRY = 10; private URL url; private HttpURLConnection httpConnection; private InputStream in_stream; private long cur_pos; //决定seek方法中是否执行文件读取定位 private int connect_timeout; private int read_timeout; public HttpReader(URL u) { this(u, 5000, 5000); } public HttpReader(URL u, int connect_timeout, int read_timeout) { this.connect_timeout = connect_timeout; this.read_timeout = read_timeout; url = u; } public int read(byte[] b, int off, int len) throws IOException { int r = in_stream.read(b, off, len); cur_pos += r; return r; } /* * 抛出异常通知重试. * 例如响应码503可能是由某种暂时的原因引起的,同一IP频繁的连接请求会遭服务器拒绝. */ public void seek(long start_pos) throws IOException { if (start_pos == cur_pos && in_stream != null) return; if (httpConnection != null) { httpConnection.disconnect(); httpConnection = null; } if (in_stream != null) { in_stream.close(); in_stream = null; } httpConnection = (HttpURLConnection) url.openConnection(); httpConnection.setConnectTimeout(connect_timeout); httpConnection.setReadTimeout(read_timeout); String sProperty = "bytes=" + start_pos + "-"; httpConnection.setRequestProperty("Range", sProperty); //httpConnection.setRequestProperty("Connection", "Keep-Alive"); int responseCode = httpConnection.getResponseCode(); if (responseCode < 200 || responseCode >= 300) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } throw new IOException("HTTP responseCode="+responseCode); } in_stream = httpConnection.getInputStream(); cur_pos = start_pos; } }
二、IWriterCallBack接口
package instream; /* * 读/写通信接口.类似于C++的回调函数 * * 例: * class BuffRandAcceURL 内实现本接口的方法tryWriting() * class BuffRandAcceURL 内new Writer(this, ...)传值到Writer.icb * class Writer 内调用icb.tryWriting() */ public interface IWriterCallBack { public int tryWriting() throws InterruptedException; public void updateBuffer(int i, int len); public void updateWriterCount(); public int getWriterCount(); public void terminateWriters(); }
三、Writer类:下载线程,负责向buf[]写数据。
package instream; import java.net.URL; public final class Writer implements Runnable { private static boolean isalive = true; // 一个线程超时其它线程也能退出 private static byte[] buf; private static IWriterCallBack icb; private HttpReader hr; public Writer(IWriterCallBack cb, URL u, byte[] b, int i) { hr = new HttpReader(u); icb = cb; buf = b; Thread t = new Thread(this,"dt_"+i); t.setPriority(Thread.NORM_PRIORITY + 1); t.start(); } public void run() { int wbytes=0, wpos=0, rema = 0, retry = 0; int idxmask = BuffRandAcceURL.UNIT_COUNT - 1; boolean cont = true; int index = 0; //buf[]内"块"索引号 int startpos = 0; //index对应的文件位置(相对于文件首的偏移量) long time0 = 0; while (cont) { try { // 1.等待空闲块 if(retry == 0) { if ((startpos = icb.tryWriting()) == -1) break; index = (startpos >> BuffRandAcceURL.UNIT_LENGTH_BITS) & idxmask; wpos = startpos & BuffRandAcceURL.BUF_LENGTH_MASK; wbytes = 0; rema = BuffRandAcceURL.UNIT_LENGTH; time0 = System.currentTimeMillis(); } // 2.定位 hr.seek(startpos); // 3.下载"一块" int w; while (rema > 0 && isalive) { w = (rema < 2048) ? rema : 2048; //每次读几K合适? if ((w = hr.read(buf, wpos, w)) == -1) { cont = false; break; } rema -= w; wpos += w; startpos += w; // 能断点续传 wbytes += w; } // 下载速度足够快就结束本线程 long t = System.currentTimeMillis() - time0; if(icb.getWriterCount() > 1 && t < 500) cont = false; //4.通知"读"线程 retry = 0; icb.updateBuffer(index, wbytes); } catch (Exception e) { if(++retry == HttpReader.MAX_RETRY) { isalive = false; icb.terminateWriters(); break; } } } icb.updateWriterCount(); try { hr.close(); } catch (Exception e) {} hr = null; } }
四、BuffRandAcceURL类功能:创建下载线程;read方法从buf[]读数据。关键是如何简单有效防止死锁?
package instream; import java.net.URL; public final class BuffRandAcceURL implements IWriterCallBack { public static final int UNIT_LENGTH_BITS = 15; public static final int UNIT_LENGTH = 1 << UNIT_LENGTH_BITS; //2^16=32K public static final int BUF_LENGTH = UNIT_LENGTH << 4; public static final int UNIT_COUNT = BUF_LENGTH >> UNIT_LENGTH_BITS; //16块 public static final int BUF_LENGTH_MASK = (BUF_LENGTH - 1); private static final int MAX_WRITER = 5; private static long file_pointer; private static int read_pos; private static int fill_bytes; private static byte[] buf; //同时作写线程同步锁 private static int[] unit_bytes; //同时作读线程互斥锁 private static int alloc_pos; private static URL url; private static boolean isalive = true; private static int writer_count; private static long file_length; private static long frame_bytes; private static int free_unit = UNIT_COUNT; // "信号量"计数器 public BuffRandAcceURL(String sURL) throws Exception { this(sURL,MAX_WRITER); } public BuffRandAcceURL(String sURL, int download_threads) throws Exception { buf = new byte[BUF_LENGTH]; unit_bytes = new int[UNIT_COUNT]; url = new URL(sURL); // 创建"写"线程 writer_count = download_threads; for (int i = 0; i < download_threads; i++) { new Writer(this, url, buf, i + 1); Thread.sleep(200); } try_cache(); } /* * 缓冲 */ private void try_cache() throws InterruptedException { int cache_size = BUF_LENGTH; int bi = unit_bytes[read_pos >> UNIT_LENGTH_BITS]; if(bi != 0) cache_size -= UNIT_LENGTH - bi; while (fill_bytes < cache_size) { if (writer_count == 0 || isalive == false) return; synchronized (unit_bytes) { unit_bytes.wait(200); //wait(200)错过通知也可结束循环? } } } private int try_reading(int i, int len) throws Exception { int n = (i + 1) & (UNIT_COUNT - 1); int r = (unit_bytes[i] > 0) ? (unit_bytes[i] + unit_bytes[n]) : unit_bytes[i]; if (r < len) { if (writer_count == 0 || isalive == false) return r; try_cache(); } return len; } /* * 各个"写"线程互斥等待空闲块. 空闲块按由小到大的顺序分配. */ public int tryWriting() throws InterruptedException { int ret = -1; synchronized (buf) { while (free_unit == 0 && isalive) buf.wait(); if(alloc_pos >= file_length || isalive == false) return -1; ret = alloc_pos; alloc_pos += UNIT_LENGTH; free_unit--; } return ret; } /* * "写"线程向buf[]写完数据后调用,通知"读"线程 */ public void updateBuffer(int i, int len) { synchronized (unit_bytes) { unit_bytes[i] = len; fill_bytes += len; unit_bytes.notify(); } } /* * "写"线程准备退出时调用 */ public void updateWriterCount() { synchronized (unit_bytes) { writer_count--; unit_bytes.notify(); } } public int getWriterCount() { return writer_count; } /* * read方法内调用 */ public void notifyWriter() { synchronized (buf) { buf.notifyAll(); } } /* * 被某个"写"线程调用,用于终止其它"写"线程;isalive也影响"读"线程流程 */ public void terminateWriters() { synchronized (unit_bytes) { if (isalive) { isalive = false; System.out.println("\n读取文件超时。重试 " + HttpReader.MAX_RETRY + " 次后放弃,请您稍后再试。"); } unit_bytes.notify(); } notifyWriter(); } public int read(byte[] b, int off, int len) throws Exception { int i = read_pos >> UNIT_LENGTH_BITS; // 1.等待有足够内容可读 if(try_reading(i, len) < len || isalive == false) return -1; // 2.读取 int tail = BUF_LENGTH - read_pos; // write_pos != BUF_LENGTH if (tail < len) { System.arraycopy(buf, read_pos, b, off, tail); System.arraycopy(buf, 0, b, off + tail, len - tail); } else System.arraycopy(buf, read_pos, b, off, len); fill_bytes -= len; file_pointer += len; read_pos += len; read_pos &= BUF_LENGTH_MASK; unit_bytes[i] -= len; if (unit_bytes[i] < 0) { int ni = read_pos >> UNIT_LENGTH_BITS; unit_bytes[ni] += unit_bytes[i]; unit_bytes[i] = 0; free_unit++; notifyWriter(); } else if (unit_bytes[i] == 0) { free_unit++; // 空闲块"信号量"计数加1 notifyWriter(); // 3.通知 } // 如果下一块未填满,意味着文件读完,第1步已处理一次读空两块的情况 return len; } }
本文是JAVA开源项目jmp123源代码的一部分,单独成文旨在与朋友们交流文中提出的问题模型的解决方法。
【jmp123下载地址】http://jmp123.sourceforge.net/
评论
<div class="quote_div">最好能贴一下使用代码,否则真的很难有耐心看下去。 有无从入手的感觉 </div>
<p> </p>
<div class="quote_title">xiayh1002 写道</div>
<div class="quote_div">代码最好能下载运行一下,多线程光这么看代码,还比较难理解</div>
<p> </p>
<div class="quote_title">beginLi 写道</div>
<div class="quote_div">楼主 开源是有好处的..你的播放器就舍不得拿出来? </div>
<p> </p>
<p>这儿中介对以缓冲方式读取远程文件的方法的一介简要介绍。更详细的讲解在本站论坛和本站我的博客上有,如果你有兴趣,请看《(四)用JAVA编写MP3解码器——读取文件》</p>
<p>本站论坛 <a href="http://www.iteye.com/topic/740090">http://www.iteye.com/topic/740090</a></p>
<p>本站博客 <a href="http://lfp001.iteye.com/blog/740090">http://lfp001.iteye.com/blog/740090</a></p>
+1.变量命名不符java的规范。
发表评论
-
(附)用JAVA编写MP3解码器——GUI
2010-12-24 19:32 4362以下代码是开源(GPL)程序jmp123的一部分。 ... -
(十八)用JAVA编写MP3解码器——迷你播放器
2010-08-30 18:01 58161.定义解码一帧的接口 ILayer123 ... -
(十七)用JAVA编写MP3解码器——解码Layer1
2010-08-29 14:32 3899Layer1的编码更简单,解码端的代码也就比La ... -
(十六)用JAVA编写MP3解码器——解码Layer2
2010-08-29 14:17 4169MPEG 1.0/2.0/2.5 对声音的压缩分 ... -
(十五)用JAVA编写MP3解码器——音频输出
2010-08-29 13:40 5406解码一帧Layer3第10步:音频输出 -- class ... -
(十四)用JAVA编写MP3解码器——多相合成滤波
2010-08-29 10:03 3596解码一帧Layer3第8步:多相频率倒置 (Inverse ... -
(十三)用JAVA编写MP3解码器——IMDCT快速算法
2010-08-28 18:14 2914解码一帧Layer3第7步:IMDCT和子带混合 -- cl ... -
(十二)用JAVA编写MP3解码器——消混叠处理
2010-08-26 19:37 2508解码一帧Layer3第6步:消混叠处理 -- class L ... -
(十一)用JAVA编写MP3解码器——立体声处理
2010-08-25 14:32 2928解码一帧Layer3第5步: ... -
(十)用JAVA编写MP3解码器——逆量化和重排序
2010-08-22 15:20 3175解码一帧Layer3第4步:逆量化和重排序 -- requa ... -
(九)用JAVA编写MP3解码器——哈夫曼解码
2010-08-21 16:21 3613解码一帧Layer3第3步:哈夫曼解码 -- huffman ... -
(八)用JAVA编写MP3解码器——解码增益因子
2010-08-20 15:39 2414解码一帧Layer3第2步:解码增益因子 -- getSc ... -
(七)用JAVA编写MP3解码器——解码帧边信息
2010-08-20 11:39 2775解码一帧Layer3第1步: ... -
(六)用JAVA编写MP3解码器——帧数据结构
2010-08-19 20:39 3452MP3文件按帧(frame)依次存储,解码时也是 ... -
(五)用JAVA编写MP3解码器——解析文件信息
2010-08-19 11:34 4973前文提到解析MP3标签,程序源码中也已经出现了调 ... -
(四)用JAVA编写MP3解码器——读取文件
2010-08-18 13:28 36821.随机文件访问接口 对MP3解码时需要随机读 ... -
(三)用JAVA编写MP3解码器——读取位流
2010-08-17 23:17 3499文件以字节为单位读取,MP3解码器输入的数据是位流,即每 ... -
(二)用JAVA编写MP3解码器——帧头信息解码
2010-08-17 21:43 66551.解析帧头 帧头共4字节,从高位到低位这32比特的含 ... -
(一)用JAVA编写MP3解码器——前言
2010-08-17 21:04 19023【内容提要】 《用JAVA编写MP3解码器》系列文章将 ... -
JAVA实现位流缓冲区
2009-06-22 11:03 683原理很简单,关键是效率。以流媒体解码为例,一次读几比特 ...
相关推荐
嵌入式八股文面试题库资料知识宝典-华为的面试试题.zip
训练导控系统设计.pdf
嵌入式八股文面试题库资料知识宝典-网络编程.zip
人脸转正GAN模型的高效压缩.pdf
少儿编程scratch项目源代码文件案例素材-几何冲刺 转瞬即逝.zip
少儿编程scratch项目源代码文件案例素材-鸡蛋.zip
嵌入式系统_USB设备枚举与HID通信_CH559单片机USB主机键盘鼠标复合设备控制_基于CH559单片机的USB主机模式设备枚举与键盘鼠标数据收发系统支持复合设备识别与HID
嵌入式八股文面试题库资料知识宝典-linux常见面试题.zip
面向智慧工地的压力机在线数据的预警应用开发.pdf
基于Unity3D的鱼类运动行为可视化研究.pdf
少儿编程scratch项目源代码文件案例素材-霍格沃茨魔法学校.zip
少儿编程scratch项目源代码文件案例素材-金币冲刺.zip
内容概要:本文深入探讨了HarmonyOS编译构建子系统的作用及其技术细节。作为鸿蒙操作系统背后的关键技术之一,编译构建子系统通过GN和Ninja工具实现了高效的源代码到机器代码的转换,确保了系统的稳定性和性能优化。该系统不仅支持多系统版本构建、芯片厂商定制,还具备强大的调试与维护能力。其高效编译速度、灵活性和可扩展性使其在华为设备和其他智能终端中发挥了重要作用。文章还比较了HarmonyOS编译构建子系统与安卓和iOS编译系统的异同,并展望了其未来的发展趋势和技术演进方向。; 适合人群:对操作系统底层技术感兴趣的开发者、工程师和技术爱好者。; 使用场景及目标:①了解HarmonyOS编译构建子系统的基本概念和工作原理;②掌握其在不同设备上的应用和优化策略;③对比HarmonyOS与安卓、iOS编译系统的差异;④探索其未来发展方向和技术演进路径。; 其他说明:本文详细介绍了HarmonyOS编译构建子系统的架构设计、核心功能和实际应用案例,强调了其在万物互联时代的重要性和潜力。阅读时建议重点关注编译构建子系统的独特优势及其对鸿蒙生态系统的深远影响。
嵌入式八股文面试题库资料知识宝典-奇虎360 2015校园招聘C++研发工程师笔试题.zip
嵌入式八股文面试题库资料知识宝典-腾讯2014校园招聘C语言笔试题(附答案).zip
双种群变异策略改进RWCE算法优化换热网络.pdf
内容概要:本文详细介绍了基于瞬时无功功率理论的三电平有源电力滤波器(APF)仿真研究。主要内容涵盖并联型APF的工作原理、三相三电平NPC结构、谐波检测方法(ipiq)、双闭环控制策略(电压外环+电流内环PI控制)以及SVPWM矢量调制技术。仿真结果显示,在APF投入前后,电网电流THD从21.9%降至3.77%,显著提高了电能质量。 适用人群:从事电力系统研究、电力电子技术开发的专业人士,尤其是对有源电力滤波器及其仿真感兴趣的工程师和技术人员。 使用场景及目标:适用于需要解决电力系统中谐波污染和无功补偿问题的研究项目。目标是通过仿真验证APF的有效性和可行性,优化电力系统的电能质量。 其他说明:文中提到的仿真模型涉及多个关键模块,如三相交流电压模块、非线性负载、信号采集模块、LC滤波器模块等,这些模块的设计和协同工作对于实现良好的谐波抑制和无功补偿至关重要。
内容概要:本文探讨了在工业自动化和物联网交汇背景下,构建OPC DA转MQTT网关软件的需求及其具体实现方法。文中详细介绍了如何利用Python编程语言及相关库(如OpenOPC用于读取OPC DA数据,paho-mqtt用于MQTT消息传递),完成从OPC DA数据解析、格式转换到最终通过MQTT协议发布数据的关键步骤。此外,还讨论了针对不良网络环境下数据传输优化措施以及后续测试验证过程。 适合人群:从事工业自动化系统集成、物联网项目开发的技术人员,特别是那些希望提升跨协议数据交换能力的专业人士。 使用场景及目标:适用于需要在不同通信协议间建立高效稳定的数据通道的应用场合,比如制造业生产线监控、远程设备管理等。主要目的是克服传统有线网络限制,实现在不稳定无线网络条件下仍能保持良好性能的数据传输。 其他说明:文中提供了具体的代码片段帮助理解整个流程,并强调了实际部署过程中可能遇到的问题及解决方案。
基于C#实现的检测小说章节的重复、缺失、广告等功能+源码+项目文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 基于C#实现的检测小说章节的重复、缺失、广告等功能+源码+项目文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档~ 基于C#实现的检测小说章节的重复、缺失、广告等功能+源码+项目文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 基于C#实现的检测小说章节的重复、缺失、广告等功能+源码+项目文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 基于C#实现的检测小说章节的重复、缺失、广告等功能+源码+项目文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 基于C#实现的检测小说章节的重复、缺失、广告等功能+源码+项目文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档
少儿编程scratch项目源代码文件案例素材-火柴人终极之战.zip