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


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

    分类: Android 流媒体 4779人阅读 评论(10) 收藏 举报

    转载请注明出处!

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

     

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

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

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

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

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

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

     

    接下来看代码:

    编码器:

    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;代码写的很清楚详细,有疑问的可以提出。


    播放器:

    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播放器那样有缓存的,只不过我这里的处理得挺粗糙。代码写的也挺详细了,如果有疑问也可以提出来。

     

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

     

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

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

     

  • 相关阅读:
    HTML标签(2)
    HTML简介(1)
    JqueryUI input 自动提示 autocomplete
    Linux基础--单ubuntu 系统 u盘启动 install
    Spark Parquet file split
    HashMap与ConcurrentHashMap
    线程池阻塞队列之ArrayBlockingQueue
    线程池阻塞队列之LinkedBlockingQueue
    线程池的拒绝策略
    关闭线程池shutdown 和 shutdownNow 的区别
  • 原文地址:https://www.cnblogs.com/songtzu/p/2939393.html
Copyright © 2020-2023  润新知