Android音频实时传输与播放(三):AMR硬编码与硬解码

原文链接 http://blog.csdn.net/zgyulongfei/article/details/7753163

Android中我所知道的音频编解码有两种方式:

(一)使用AudioRecord采集音频,用这种方式采集的是未经压缩的音频流;用AudioTrack播放实时音频流。用这两个类的话,如果需要对音频进行编解码,就需要自己移植编解码库了,比如可以移植ilbc,speex等开源编解码库。

 ilbc的编解码实现可以查看这个专栏:http://blog.csdn.net/column/details/media.html

(二)使用MediaRecorder获取编码后的AMR音频,但由于MediaRecorder的特点,只能将流保存到文件中,但通过其他方式是可以获取到实时音频流的,这篇文章将介绍用LocalSocket的方法来实现;使用MediaPlayer来播放AMR音频流,但同样MediaPlayer也只能播放文件流,因此我用缓存的方式来播放音频。

以上两种方式各有利弊,使用方法(一)需移植编解码库,但可以播放实时音频流;使用方法(二)直接硬编硬解码效率高,但是需要对文件进行操作。

PS:这篇文章只是给大家一个参考,仅供学习之用,如果真正用到项目中还有很多地方需要优化。

我强烈推荐播放音频时候用方法(一),方法(二)虽然能够实现功能,但是实现方式不太好。


接下来看代码:

编码器:

[java]  view plain  copy
  1. package cn.edu.xmu.zgy.audio.encoder;  
  2.   
  3. import java.io.DataInputStream;  
  4. import java.io.IOException;  
  5. import java.net.DatagramPacket;  
  6. import java.net.DatagramSocket;  
  7. import java.net.InetAddress;  
  8.   
  9. import cn.edu.xmu.zgy.config.CommonConfig;  
  10.   
  11. import android.app.Activity;  
  12. import android.media.MediaRecorder;  
  13. import android.net.LocalServerSocket;  
  14. import android.net.LocalSocket;  
  15. import android.net.LocalSocketAddress;  
  16. import android.util.Log;  
  17. import android.widget.Toast;  
  18.   
  19. //blog.csdn.net/zgyulongfei  
  20. //Email: zgyulongfei@gmail.com  
  21.   
  22. public class AmrAudioEncoder {  
  23.     private static final String TAG = "ArmAudioEncoder";  
  24.   
  25.     private static AmrAudioEncoder amrAudioEncoder = null;  
  26.   
  27.     private Activity activity;  
  28.   
  29.     private MediaRecorder audioRecorder;  
  30.   
  31.     private boolean isAudioRecording;  
  32.   
  33.     private LocalServerSocket lss;  
  34.     private LocalSocket sender, receiver;  
  35.   
  36.     private AmrAudioEncoder() {  
  37.     }  
  38.   
  39.     public static AmrAudioEncoder getArmAudioEncoderInstance() {  
  40.         if (amrAudioEncoder == null) {  
  41.             synchronized (AmrAudioEncoder.class) {  
  42.                 if (amrAudioEncoder == null) {  
  43.                     amrAudioEncoder = new AmrAudioEncoder();  
  44.                 }  
  45.             }  
  46.         }  
  47.         return amrAudioEncoder;  
  48.     }  
  49.   
  50.     public void initArmAudioEncoder(Activity activity) {  
  51.         this.activity = activity;  
  52.         isAudioRecording = false;  
  53.     }  
  54.   
  55.     public void start() {  
  56.         if (activity == null) {  
  57.             showToastText("音频编码器未初始化,请先执行init方法");  
  58.             return;  
  59.         }  
  60.   
  61.         if (isAudioRecording) {  
  62.             showToastText("音频已经开始编码,无需再次编码");  
  63.             return;  
  64.         }  
  65.   
  66.         if (!initLocalSocket()) {  
  67.             showToastText("本地服务开启失败");  
  68.             releaseAll();  
  69.             return;  
  70.         }  
  71.   
  72.         if (!initAudioRecorder()) {  
  73.             showToastText("音频编码器初始化失败");  
  74.             releaseAll();  
  75.             return;  
  76.         }  
  77.   
  78.         this.isAudioRecording = true;  
  79.         startAudioRecording();  
  80.     }  
  81.   
  82.     private boolean initLocalSocket() {  
  83.         boolean ret = true;  
  84.         try {  
  85.             releaseLocalSocket();  
  86.   
  87.             String serverName = "armAudioServer";  
  88.             final int bufSize = 1024;  
  89.   
  90.             lss = new LocalServerSocket(serverName);  
  91.   
  92.             receiver = new LocalSocket();  
  93.             receiver.connect(new LocalSocketAddress(serverName));  
  94.             receiver.setReceiveBufferSize(bufSize);  
  95.             receiver.setSendBufferSize(bufSize);  
  96.   
  97.             sender = lss.accept();  
  98.             sender.setReceiveBufferSize(bufSize);  
  99.             sender.setSendBufferSize(bufSize);  
  100.         } catch (IOException e) {  
  101.             ret = false;  
  102.         }  
  103.         return ret;  
  104.     }  
  105.   
  106.     private boolean initAudioRecorder() {  
  107.         if (audioRecorder != null) {  
  108.             audioRecorder.reset();  
  109.             audioRecorder.release();  
  110.         }  
  111.         audioRecorder = new MediaRecorder();  
  112.         audioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  113.         audioRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);  
  114.         final int mono = 1;  
  115.         audioRecorder.setAudioChannels(mono);  
  116.         audioRecorder.setAudioSamplingRate(8000);  
  117.         audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  118.         audioRecorder.setOutputFile(sender.getFileDescriptor());  
  119.   
  120.         boolean ret = true;  
  121.         try {  
  122.             audioRecorder.prepare();  
  123.             audioRecorder.start();  
  124.         } catch (Exception e) {  
  125.             releaseMediaRecorder();  
  126.             showToastText("手机不支持录音此功能");  
  127.             ret = false;  
  128.         }  
  129.         return ret;  
  130.     }  
  131.   
  132.     private void startAudioRecording() {  
  133.         new Thread(new AudioCaptureAndSendThread()).start();  
  134.     }  
  135.   
  136.     public void stop() {  
  137.         if (isAudioRecording) {  
  138.             isAudioRecording = false;  
  139.         }  
  140.         releaseAll();  
  141.     }  
  142.   
  143.     private void releaseAll() {  
  144.         releaseMediaRecorder();  
  145.         releaseLocalSocket();  
  146.         amrAudioEncoder = null;  
  147.     }  
  148.   
  149.     private void releaseMediaRecorder() {  
  150.         try {  
  151.             if (audioRecorder == null) {  
  152.                 return;  
  153.             }  
  154.             if (isAudioRecording) {  
  155.                 audioRecorder.stop();  
  156.                 isAudioRecording = false;  
  157.             }  
  158.             audioRecorder.reset();  
  159.             audioRecorder.release();  
  160.             audioRecorder = null;  
  161.         } catch (Exception err) {  
  162.             Log.d(TAG, err.toString());  
  163.         }  
  164.     }  
  165.   
  166.     private void releaseLocalSocket() {  
  167.         try {  
  168.             if (sender != null) {  
  169.                 sender.close();  
  170.             }  
  171.             if (receiver != null) {  
  172.                 receiver.close();  
  173.             }  
  174.             if (lss != null) {  
  175.                 lss.close();  
  176.             }  
  177.         } catch (IOException e) {  
  178.             e.printStackTrace();  
  179.         }  
  180.         sender = null;  
  181.         receiver = null;  
  182.         lss = null;  
  183.     }  
  184.   
  185.     private boolean isAudioRecording() {  
  186.         return isAudioRecording;  
  187.     }  
  188.   
  189.     private void showToastText(String msg) {  
  190.         Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();  
  191.     }  
  192.   
  193.     private class AudioCaptureAndSendThread implements Runnable {  
  194.         public void run() {  
  195.             try {  
  196.                 sendAmrAudio();  
  197.             } catch (Exception e) {  
  198.                 Log.e(TAG, "sendAmrAudio() 出错");  
  199.             }  
  200.         }  
  201.   
  202.         private void sendAmrAudio() throws Exception {  
  203.             DatagramSocket udpSocket = new DatagramSocket();  
  204.             DataInputStream dataInput = new DataInputStream(receiver.getInputStream());  
  205.   
  206.             skipAmrHead(dataInput);  
  207.   
  208.             final int SEND_FRAME_COUNT_ONE_TIME = 10;// 每次发送10帧的数据,1帧大约32B  
  209.             // AMR格式见博客:http://blog.csdn.net/dinggo/article/details/1966444  
  210.             final int BLOCK_SIZE[] = { 121315171920263150000000 };  
  211.   
  212.             byte[] sendBuffer = new byte[1024];  
  213.             while (isAudioRecording()) {  
  214.                 int offset = 0;  
  215.                 for (int index = 0; index < SEND_FRAME_COUNT_ONE_TIME; ++index) {  
  216.                     if (!isAudioRecording()) {  
  217.                         break;  
  218.                     }  
  219.                     dataInput.read(sendBuffer, offset, 1);  
  220.                     int blockIndex = (int) (sendBuffer[offset] >> 3) & 0x0F;  
  221.                     int frameLength = BLOCK_SIZE[blockIndex];  
  222.                     readSomeData(sendBuffer, offset + 1, frameLength, dataInput);  
  223.                     offset += frameLength + 1;  
  224.                 }  
  225.                 udpSend(udpSocket, sendBuffer, offset);  
  226.             }  
  227.             udpSocket.close();  
  228.             dataInput.close();  
  229.             releaseAll();  
  230.         }  
  231.   
  232.         private void skipAmrHead(DataInputStream dataInput) {  
  233.             final byte[] AMR_HEAD = new byte[] { 0x230x210x410x4D0x520x0A };  
  234.             int result = -1;  
  235.             int state = 0;  
  236.             try {  
  237.                 while (-1 != (result = dataInput.readByte())) {  
  238.                     if (AMR_HEAD[0] == result) {  
  239.                         state = (0 == state) ? 1 : 0;  
  240.                     } else if (AMR_HEAD[1] == result) {  
  241.                         state = (1 == state) ? 2 : 0;  
  242.                     } else if (AMR_HEAD[2] == result) {  
  243.                         state = (2 == state) ? 3 : 0;  
  244.                     } else if (AMR_HEAD[3] == result) {  
  245.                         state = (3 == state) ? 4 : 0;  
  246.                     } else if (AMR_HEAD[4] == result) {  
  247.                         state = (4 == state) ? 5 : 0;  
  248.                     } else if (AMR_HEAD[5] == result) {  
  249.                         state = (5 == state) ? 6 : 0;  
  250.                     }  
  251.   
  252.                     if (6 == state) {  
  253.                         break;  
  254.                     }  
  255.                 }  
  256.             } catch (Exception e) {  
  257.                 Log.e(TAG, "read mdat error...");  
  258.             }  
  259.         }  
  260.   
  261.         private void readSomeData(byte[] buffer, int offset, int length, DataInputStream dataInput) {  
  262.             int numOfRead = -1;  
  263.             while (true) {  
  264.                 try {  
  265.                     numOfRead = dataInput.read(buffer, offset, length);  
  266.                     if (numOfRead == -1) {  
  267.                         Log.d(TAG, "amr...no data get wait for data coming.....");  
  268.                         Thread.sleep(100);  
  269.                     } else {  
  270.                         offset += numOfRead;  
  271.                         length -= numOfRead;  
  272.                         if (length <= 0) {  
  273.                             break;  
  274.                         }  
  275.                     }  
  276.                 } catch (Exception e) {  
  277.                     Log.e(TAG, "amr..error readSomeData");  
  278.                     break;  
  279.                 }  
  280.             }  
  281.         }  
  282.   
  283.         private void udpSend(DatagramSocket udpSocket, byte[] buffer, int sendLength) {  
  284.             try {  
  285.                 InetAddress ip = InetAddress.getByName(CommonConfig.SERVER_IP_ADDRESS.trim());  
  286.                 int port = CommonConfig.AUDIO_SERVER_UP_PORT;  
  287.   
  288.                 byte[] sendBuffer = new byte[sendLength];  
  289.                 System.arraycopy(buffer, 0, sendBuffer, 0, sendLength);  
  290.   
  291.                 DatagramPacket packet = new DatagramPacket(sendBuffer, sendLength);  
  292.                 packet.setAddress(ip);  
  293.                 packet.setPort(port);  
  294.                 udpSocket.send(packet);  
  295.             } catch (IOException e) {  
  296.                 e.printStackTrace();  
  297.             }  
  298.         }  
  299.     }  
  300. }  

关于编码器:前面提到了,MediaRecorder的硬编码的方式只能将码流保存到文件中,这里用了LocalSocket的方式将流保存到内存中,然后从缓冲中读取码流。由于保存的格式RAW_AMR格式的,因此需要对读取到的数据进行解析,从而获得真正的音频流。想了解AMR音频码流格式的,可以查看代码中附上的网页链接。由于压缩过的码流很小,因此我在实现的时候,组合了int SEND_FRAME_COUNT_ONE_TIME = 10帧的码流后才往外发送,这样的方式造成的延迟会加重,大家可以根据自己的需要进行修改。造成延迟的另一因素是LocalSocket缓冲的大小,在这里我设置的大小是final int bufSize = 1024;代码写的很清楚详细,有疑问的可以提出。


播放器:

[java]  view plain  copy
  1. package cn.edu.xmu.zgy.audio.player;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.BufferedOutputStream;  
  5. import java.io.File;  
  6. import java.io.FileInputStream;  
  7. import java.io.FileOutputStream;  
  8. import java.io.IOException;  
  9. import java.io.InputStream;  
  10. import java.net.InetAddress;  
  11. import java.net.Socket;  
  12.   
  13. import cn.edu.xmu.zgy.config.CommonConfig;  
  14.   
  15. import android.app.Activity;  
  16. import android.media.MediaPlayer;  
  17. import android.os.Handler;  
  18. import android.util.Log;  
  19.   
  20. //blog.csdn.net/zgyulongfei  
  21. //Email: zgyulongfei@gmail.com  
  22.   
  23. public class AmrAudioPlayer {  
  24.     private static final String TAG = "AmrAudioPlayer";  
  25.   
  26.     private static AmrAudioPlayer playerInstance = null;  
  27.   
  28.     private long alreadyReadByteCount = 0;  
  29.   
  30.     private MediaPlayer audioPlayer;  
  31.     private Handler handler = new Handler();  
  32.   
  33.     private final String cacheFileName = "audioCacheFile";  
  34.     private File cacheFile;  
  35.     private int cacheFileCount = 0;  
  36.   
  37.     // 用来记录是否已经从cacheFile中复制数据到另一个cache中  
  38.     private boolean hasMovedTheCacheFlag;  
  39.   
  40.     private boolean isPlaying;  
  41.     private Activity activity;  
  42.   
  43.     private boolean isChaingCacheToAnother;  
  44.   
  45.     private AmrAudioPlayer() {  
  46.     }  
  47.   
  48.     public static AmrAudioPlayer getAmrAudioPlayerInstance() {  
  49.         if (playerInstance == null) {  
  50.             synchronized (AmrAudioPlayer.class) {  
  51.                 if (playerInstance == null) {  
  52.                     playerInstance = new AmrAudioPlayer();  
  53.                 }  
  54.             }  
  55.         }  
  56.         return playerInstance;  
  57.     }  
  58.   
  59.     public void initAmrAudioPlayer(Activity activity) {  
  60.         this.activity = activity;  
  61.         deleteExistCacheFile();  
  62.         initCacheFile();  
  63.     }  
  64.   
  65.     private void deleteExistCacheFile() {  
  66.         File cacheDir = activity.getCacheDir();  
  67.         File[] needDeleteCacheFiles = cacheDir.listFiles();  
  68.         for (int index = 0; index < needDeleteCacheFiles.length; ++index) {  
  69.             File cache = needDeleteCacheFiles[index];  
  70.             if (cache.isFile()) {  
  71.                 if (cache.getName().contains(cacheFileName.trim())) {  
  72.                     Log.e(TAG, "delete cache file: " + cache.getName());  
  73.                     cache.delete();  
  74.                 }  
  75.             }  
  76.         }  
  77.         needDeleteCacheFiles = null;  
  78.     }  
  79.   
  80.     private void initCacheFile() {  
  81.         cacheFile = null;  
  82.         cacheFile = new File(activity.getCacheDir(), cacheFileName);  
  83.     }  
  84.   
  85.     public void start() {  
  86.         isPlaying = true;  
  87.         isChaingCacheToAnother = false;  
  88.         setHasMovedTheCacheToAnotherCache(false);  
  89.         new Thread(new NetAudioPlayerThread()).start();  
  90.     }  
  91.   
  92.     public void stop() {  
  93.         isPlaying = false;  
  94.         isChaingCacheToAnother = false;  
  95.         setHasMovedTheCacheToAnotherCache(false);  
  96.         releaseAudioPlayer();  
  97.         deleteExistCacheFile();  
  98.         cacheFile = null;  
  99.         handler = null;  
  100.     }  
  101.   
  102.     private void releaseAudioPlayer() {  
  103.         playerInstance = null;  
  104.         if (audioPlayer != null) {  
  105.             try {  
  106.                 if (audioPlayer.isPlaying()) {  
  107.                     audioPlayer.pause();  
  108.                 }  
  109.                 audioPlayer.release();  
  110.                 audioPlayer = null;  
  111.             } catch (Exception e) {  
  112.             }  
  113.         }  
  114.     }  
  115.   
  116.     private boolean hasMovedTheCacheToAnotherCache() {  
  117.         return hasMovedTheCacheFlag;  
  118.     }  
  119.   
  120.     private void setHasMovedTheCacheToAnotherCache(boolean result) {  
  121.         hasMovedTheCacheFlag = result;  
  122.     }  
  123.   
  124.     private class NetAudioPlayerThread implements Runnable {  
  125.         // 从接受数据开始计算,当缓存大于INIT_BUFFER_SIZE时候开始播放  
  126.         private final int INIT_AUDIO_BUFFER = 2 * 1024;  
  127.         // 剩1秒的时候播放新的缓存的音乐  
  128.         private final int CHANGE_CACHE_TIME = 1000;  
  129.   
  130.         public void run() {  
  131.             try {  
  132.                 Socket socket = createSocketConnectToServer();  
  133.                 receiveNetAudioThenPlay(socket);  
  134.             } catch (Exception e) {  
  135.                 Log.e(TAG, e.getMessage() + "从服务端接受音频失败。。。");  
  136.             }  
  137.         }  
  138.   
  139.         private Socket createSocketConnectToServer() throws Exception {  
  140.             String hostName = CommonConfig.SERVER_IP_ADDRESS;  
  141.             InetAddress ipAddress = InetAddress.getByName(hostName);  
  142.             int port = CommonConfig.AUDIO_SERVER_DOWN_PORT;  
  143.             Socket socket = new Socket(ipAddress, port);  
  144.             return socket;  
  145.         }  
  146.   
  147.         private void receiveNetAudioThenPlay(Socket socket) throws Exception {  
  148.             InputStream inputStream = socket.getInputStream();  
  149.             FileOutputStream outputStream = new FileOutputStream(cacheFile);  
  150.   
  151.             final int BUFFER_SIZE = 100 * 1024;// 100kb buffer size  
  152.             byte[] buffer = new byte[BUFFER_SIZE];  
  153.   
  154.             // 收集了10*350b了之后才开始更换缓存  
  155.             int testTime = 10;  
  156.             try {  
  157.                 alreadyReadByteCount = 0;  
  158.                 while (isPlaying) {  
  159.                     int numOfRead = inputStream.read(buffer);  
  160.                     if (numOfRead <= 0) {  
  161.                         break;  
  162.                     }  
  163.                     alreadyReadByteCount += numOfRead;  
  164.                     outputStream.write(buffer, 0, numOfRead);  
  165.                     outputStream.flush();  
  166.                     try {  
  167.                         if (testTime++ >= 10) {  
  168.                             Log.e(TAG, "cacheFile=" + cacheFile.length());  
  169.                             testWhetherToChangeCache();  
  170.                             testTime = 0;  
  171.                         }  
  172.                     } catch (Exception e) {  
  173.                         // TODO: handle exception  
  174.                     }  
  175.   
  176.                     // 如果复制了接收网络流的cache,则执行此操作  
  177.                     if (hasMovedTheCacheToAnotherCache() && !isChaingCacheToAnother) {  
  178.                         if (outputStream != null) {  
  179.                             outputStream.close();  
  180.                             outputStream = null;  
  181.                         }  
  182.                         // 将接收网络流的cache删除,然后重0开始存储  
  183.                         // initCacheFile();  
  184.                         outputStream = new FileOutputStream(cacheFile);  
  185.                         setHasMovedTheCacheToAnotherCache(false);  
  186.                         alreadyReadByteCount = 0;  
  187.                     }  
  188.   
  189.                 }  
  190.             } catch (Exception e) {  
  191.                 errorOperator();  
  192.                 e.printStackTrace();  
  193.                 Log.e(TAG, "socket disconnect...:" + e.getMessage());  
  194.                 throw new Exception("socket disconnect....");  
  195.             } finally {  
  196.                 buffer = null;  
  197.                 if (socket != null) {  
  198.                     socket.close();  
  199.                 }  
  200.                 if (inputStream != null) {  
  201.                     inputStream.close();  
  202.                     inputStream = null;  
  203.                 }  
  204.                 if (outputStream != null) {  
  205.                     outputStream.close();  
  206.                     outputStream = null;  
  207.                 }  
  208.                 stop();  
  209.             }  
  210.         }  
  211.   
  212.         private void testWhetherToChangeCache() throws Exception {  
  213.             if (audioPlayer == null) {  
  214.                 firstTimeStartPlayer();  
  215.             } else {  
  216.                 changeAnotherCacheWhenEndOfCurrentCache();  
  217.             }  
  218.         }  
  219.   
  220.         private void firstTimeStartPlayer() throws Exception {  
  221.             // 当缓存已经大于INIT_AUDIO_BUFFER则开始播放  
  222.             if (alreadyReadByteCount >= INIT_AUDIO_BUFFER) {  
  223.                 Runnable r = new Runnable() {  
  224.                     public void run() {  
  225.                         try {  
  226.                             File firstCacheFile = createFirstCacheFile();  
  227.                             // 设置已经从cache中复制数据,然后会删除这个cache  
  228.                             setHasMovedTheCacheToAnotherCache(true);  
  229.                             audioPlayer = createAudioPlayer(firstCacheFile);  
  230.                             audioPlayer.start();  
  231.                         } catch (Exception e) {  
  232.                             Log.e(TAG, e.getMessage() + " :in firstTimeStartPlayer() fun");  
  233.                         } finally {  
  234.                         }  
  235.                     }  
  236.                 };  
  237.                 handler.post(r);  
  238.             }  
  239.         }  
  240.   
  241.         private File createFirstCacheFile() throws Exception {  
  242.             String firstCacheFileName = cacheFileName + (cacheFileCount++);  
  243.             File firstCacheFile = new File(activity.getCacheDir(), firstCacheFileName);  
  244.             // 为什么不直接播放cacheFile,而要复制cacheFile到一个新的cache,然后播放此新的cache?  
  245.             // 是为了防止潜在的读/写错误,可能在写入cacheFile的时候,  
  246.             // MediaPlayer正试图读数据, 这样可以防止死锁的发生。  
  247.             moveFile(cacheFile, firstCacheFile);  
  248.             return firstCacheFile;  
  249.   
  250.         }  
  251.   
  252.         private void moveFile(File oldFile, File newFile) throws IOException {  
  253.             if (!oldFile.exists()) {  
  254.                 throw new IOException("oldFile is not exists. in moveFile() fun");  
  255.             }  
  256.             if (oldFile.length() <= 0) {  
  257.                 throw new IOException("oldFile size = 0. in moveFile() fun");  
  258.             }  
  259.             BufferedInputStream reader = new BufferedInputStream(new FileInputStream(oldFile));  
  260.             BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(newFile,  
  261.                     false));  
  262.   
  263.             final byte[] AMR_HEAD = new byte[] { 0x230x210x410x4D0x520x0A };  
  264.             writer.write(AMR_HEAD, 0, AMR_HEAD.length);  
  265.             writer.flush();  
  266.   
  267.             try {  
  268.                 byte[] buffer = new byte[1024];  
  269.                 int numOfRead = 0;  
  270.                 Log.d(TAG, "POS...newFile.length=" + newFile.length() + "  old=" + oldFile.length());  
  271.                 while ((numOfRead = reader.read(buffer, 0, buffer.length)) != -1) {  
  272.                     writer.write(buffer, 0, numOfRead);  
  273.                     writer.flush();  
  274.                 }  
  275.                 Log.d(TAG, "POS..AFTER...newFile.length=" + newFile.length());  
  276.             } catch (IOException e) {  
  277.                 Log.e(TAG, "moveFile error.. in moveFile() fun." + e.getMessage());  
  278.                 throw new IOException("moveFile error.. in moveFile() fun.");  
  279.             } finally {  
  280.                 if (reader != null) {  
  281.                     reader.close();  
  282.                     reader = null;  
  283.                 }  
  284.                 if (writer != null) {  
  285.                     writer.close();  
  286.                     writer = null;  
  287.                 }  
  288.             }  
  289.         }  
  290.   
  291.         private MediaPlayer createAudioPlayer(File audioFile) throws IOException {  
  292.             MediaPlayer mPlayer = new MediaPlayer();  
  293.   
  294.             // It appears that for security/permission reasons, it is better to  
  295.             // pass  
  296.             // a FileDescriptor rather than a direct path to the File.  
  297.             // Also I have seen errors such as "PVMFErrNotSupported" and  
  298.             // "Prepare failed.: status=0x1" if a file path String is passed to  
  299.             // setDataSource(). So unless otherwise noted, we use a  
  300.             // FileDescriptor here.  
  301.             FileInputStream fis = new FileInputStream(audioFile);  
  302.             mPlayer.reset();  
  303.             mPlayer.setDataSource(fis.getFD());  
  304.             mPlayer.prepare();  
  305.             return mPlayer;  
  306.         }  
  307.   
  308.         private void changeAnotherCacheWhenEndOfCurrentCache() throws IOException {  
  309.             // 检查当前cache剩余时间  
  310.             long theRestTime = audioPlayer.getDuration() - audioPlayer.getCurrentPosition();  
  311.             Log.e(TAG, "theRestTime=" + theRestTime + "  isChaingCacheToAnother="  
  312.                     + isChaingCacheToAnother);  
  313.             if (!isChaingCacheToAnother && theRestTime <= CHANGE_CACHE_TIME) {  
  314.                 isChaingCacheToAnother = true;  
  315.   
  316.                 Runnable r = new Runnable() {  
  317.                     public void run() {  
  318.                         try {  
  319.                             File newCacheFile = createNewCache();  
  320.                             // 设置已经从cache中复制数据,然后会删除这个cache  
  321.                             setHasMovedTheCacheToAnotherCache(true);  
  322.                             transferNewCacheToAudioPlayer(newCacheFile);  
  323.                         } catch (Exception e) {  
  324.                             Log.e(TAG, e.getMessage()  
  325.                                     + ":changeAnotherCacheWhenEndOfCurrentCache() fun");  
  326.                         } finally {  
  327.                             deleteOldCache();  
  328.                             isChaingCacheToAnother = false;  
  329.                         }  
  330.                     }  
  331.                 };  
  332.                 handler.post(r);  
  333.             }  
  334.         }  
  335.   
  336.         private File createNewCache() throws Exception {  
  337.             // 将保存网络数据的cache复制到newCache中进行播放  
  338.             String newCacheFileName = cacheFileName + (cacheFileCount++);  
  339.             File newCacheFile = new File(activity.getCacheDir(), newCacheFileName);  
  340.             Log.e(TAG, "before moveFile............the size=" + cacheFile.length());  
  341.             moveFile(cacheFile, newCacheFile);  
  342.             return newCacheFile;  
  343.         }  
  344.   
  345.         private void transferNewCacheToAudioPlayer(File newCacheFile) throws Exception {  
  346.             MediaPlayer oldPlayer = audioPlayer;  
  347.   
  348.             try {  
  349.                 audioPlayer = createAudioPlayer(newCacheFile);  
  350.                 audioPlayer.start();  
  351.             } catch (Exception e) {  
  352.                 Log.e(TAG, "filename=" + newCacheFile.getName() + " size=" + newCacheFile.length());  
  353.                 Log.e(TAG, e.getMessage() + " " + e.getCause() + " error start..in transfanNer..");  
  354.             }  
  355.             try {  
  356.                 oldPlayer.pause();  
  357.                 oldPlayer.reset();  
  358.                 oldPlayer.release();  
  359.             } catch (Exception e) {  
  360.                 Log.e(TAG, "ERROR release oldPlayer.");  
  361.             } finally {  
  362.                 oldPlayer = null;  
  363.             }  
  364.         }  
  365.   
  366.         private void deleteOldCache() {  
  367.             int oldCacheFileCount = cacheFileCount - 1;  
  368.             String oldCacheFileName = cacheFileName + oldCacheFileCount;  
  369.             File oldCacheFile = new File(activity.getCacheDir(), oldCacheFileName);  
  370.             if (oldCacheFile.exists()) {  
  371.                 oldCacheFile.delete();  
  372.             }  
  373.         }  
  374.   
  375.         private void errorOperator() {  
  376.         }  
  377.     }  
  378.   
  379. }  

关于播放器:由于MediaPlayer的限制,我用了cache的方式来实现音频的实时播放。即把获取到的音频流首先保存到文件中,然后当保存到一定大小的时候就播放之,类似于QQ播放器那样有缓存的,只不过我这里的处理得挺粗糙。代码写的也挺详细了,如果有疑问也可以提出来。


注:编码器和播放器的编写,我都是站在巨人的肩膀上完成的,参考了一些其他资料。


在后面一篇文章中,我将附上服务器和客户端的所有代码。

希望朋友们看完能提出意见和建议,也希望看完能有所收获 ^_^

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值