android http传输的音频流

在线音频常用的传输方式之一就是http,包括服务器推送等许多分支。

http实现在线音频流的主要方法是由 Nullsoft公司开发的,后被美国在线收购,Nullsoft公司是WinAMP的创作者,同时他们设立了一个名叫做SHOUTcast的http音频流在线的服务器,支持ICY协议(http的扩展协议)大部分服务器和播放软件产品都支持该协议。

android的MediaPlayer也支持ICY协议,

浏览器不直接支持ICY流,需要辅助程序来播放。


在使用ICY流时,Internet广播电台会发送一个特殊文件,一般会是一个M3U文件或者是一个PLS文件。

  • PLS文件通常是一个多媒体播放列表文件,MIME类型为: audio / x-scpls
  • M3U文件是一个存储多媒体播放列表文的件  MIME类型为:audio/x-mpegurl

M3U文件内容:

         #EXTM3U     //是必须的,指向一个M3U文件

          #EXTINF:0,Live Stream Name      //#EXTINF:开始,下来是 以秒为单位的持续时间  然后是多媒体名称

          http://www.nostreamhere.org:8000/   


android的MediaPlayer不能自动分析M3U文件,在android上创建基于http的流式多媒体播放器,必须手动分析


下面示例是一个播放联机广播电台的M3U文件


<uses-permission android:name="android.permission.INTERNET"/>


实例中有个错误就是网络地址的问题,服务器不正确也无法解析出来
package com.example.day605;

import android.media.MediaPlayer;
import android.net.http.HttpResponseCache;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Vector;

/**
 * 通过http获得mp3文件播放,
 * 该音频文件存在于服务器的位置 http://www.mobvcasting.com/android/audio/goodmorinigandroid.mp3
 */
public class MainActivity extends AppCompatActivity implements MediaPlayer.OnCompletionListener,
        View.OnClickListener, MediaPlayer.OnPreparedListener,
        MediaPlayer.OnErrorListener, MediaPlayer.OnBufferingUpdateListener {

    private MediaPlayer mediaPlayer;
    private TextView bufferText, statusText;
    private Button start, stop, parse;
    private EditText editTextUrl;
    private Vector playListItem;        //保存播放列表的中的列表条目
    //    private String url = "http://www.mobvcasting.com/android/audio/goodmorinigandroid.mp3";
    private String baseUrl;         //只想一个包含M2U文件的URL
    private int currentPlayListItem = 0;//跟踪目前处于PlayListItem的哪一条


    /**
     * 接收网络请求数据
     */
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //在MainThread线程中进行http请求时,加入可以解决主线程阻塞的异常,一般不建议将网络请求放在主线程中,可以使用在子线程里使用
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads().detectDiskWrites().detectNetwork()
                .penaltyLog().build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
                .penaltyLog().penaltyDeath().build());


        start = (Button) findViewById(R.id.start);
        stop = (Button) findViewById(R.id.stop);
        parse = (Button) findViewById(R.id.parse);
        bufferText = (TextView) findViewById(R.id.bufferText);
        statusText = (TextView) findViewById(R.id.statusText);
        editTextUrl = (EditText) findViewById(R.id.edit);

        statusText.setText("onCreate");
        //editTextUrl.setText("http://live.kboo.fm:8000/high.m3u");
        editTextUrl.setText("http://pubint.ic.llnwd.net/stream/pubint_kmfa.m3u");

        //这种无参构造的方式和以前的有参调用不同需要几个步骤
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setOnCompletionListener(this);
        mediaPlayer.setOnErrorListener(this);
        mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setOnBufferingUpdateListener(this);
        statusText.setText("MediaPalyer created");

//        try {
//            mediaPlayer.setDataSource(url);
//            statusText.setText("setDataSource done");
//            statusText.setText("calling prepareAsync");
            mediaPlayer.prepare();//运行prepare()播放器会填充一个缓冲区,即使网速慢也能平稳的播放  但这里的prepare()会造成阻塞
//            mediaPlayer.prepareAsync();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        start.setOnClickListener(this);
        stop.setOnClickListener(this);
        parse.setOnClickListener(this);
        start.setEnabled(false);
        stop.setEnabled(false);

    }

    @Override
    public void onClick(View v) {
        if (v == start) {
            playPlayListItem();  //下载M3U文件,并进行解析,选出带播放文件的行然后添加到一个playListItem向量中
//            mediaPlayer.start();
//            statusText.setText("start called");
//            start.setEnabled(false);
//            stop.setEnabled(true);
        } else if (v == stop) {
            stop();
//            mediaPlayer.pause();
//            statusText.setText("pause called");
//            start.setEnabled(true);
        } else if (v == parse) {
            parsePlaylistFile();
        }
    }

    private void stop() {
        mediaPlayer.pause();
        start.setEnabled(true);
        stop.setEnabled(false);
    }

    /**
     * 下载有editTextUrl提供的m3u文件\
     * 下载M3U文件,并进行解析,选出带播放文件的行然后添加到一个playListItem向量中
     * 在Android 6.0(API 23) 中,Google已经移除了Apache HttpClient 想关类,推荐使用HttpUrlConnection
     * 如果要继续使用在 module下的build.gradle文件中加入
     * android {
     * useLibrary 'org.apache.http.legacy'
     * }
     */
    public void parsePlaylistFile() {
        playListItem = new Vector();



        //创建HttpClient对象,类似web浏览器
        HttpClient httpClient = new DefaultHttpClient();
        //HttpGet对象表示指向一个具体的文件请求
        HttpGet getRequest = new HttpGet(editTextUrl.getText().toString());
        HttpResponse httpResponse = null;
        try {
            //httpClient执行HttpGet,返回一个HttpResponse
            httpResponse = httpClient.execute(getRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (httpResponse == null) {
            Toast toast=Toast.makeText(MainActivity.this,"解析错误",Toast.LENGTH_SHORT);
            toast.show();
            return;
        }
        if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            //错误信息
            Log.i("ERROR", "" + httpResponse.getStatusLine().getReasonPhrase());
        } else {
            InputStream inputStream = null;
            try {
                //httpResponse返回一个InputStream流
                inputStream = httpResponse.getEntity().getContent();

            } catch (IOException e) {
                e.printStackTrace();
            }
            //通过缓冲流BufferedReader,将内容读入缓存中,然后一行一行读取
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            try {
                while ((line = bufferedReader.readLine()) != null) {
                    //如果行以#开头,忽略,这些是源数据
                    if (line.startsWith("#")) {
                        //源数据做处理,目前忽略
                    } else if (line.length() > 0) {    //如果不是一个空行,即长度大于0,那么假设它是一个播放列表条目
                        String filpath = "";
                        //如果行以http://开头,将其作为流的完整URL
                        if (line.startsWith("http://")) {
                            //假设是一个完成的URL
                            filpath = line;
                        } else {
                            //否则将其作为一个相对URL,把针对该M3U文件的原始请求的URL附加上去
                            filpath = getRequest.getURI().resolve(line).toString();
                        }
                        //然后将其放在播放列表条目中
                        PlayListFile playListFile = new PlayListFile(filpath);
                        playListItem.add(playListFile);
                    }
                }
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }   //else
        //解析完成启动start按钮
        start.setEnabled(true);
    }

    /**
     * 接收playListItem'向量中的第一个条目,然后交给MediaPlayer对象做准备
     */
    private void playPlayListItem() {
        start.setEnabled(false);
        currentPlayListItem = 0;
        if (playListItem.size() > 0) {
            //获取到向量中的第一个条目
            String path = ((PlayListFile) playListItem.get(currentPlayListItem)).getFilePath();
            try {
                //将提取出的文件或流路径设置给MediaPlayer对象
                mediaPlayer.setDataSource(path);
                //准备程序,并允许MediaPlayer做缓冲    ,之后会调用onPrepared()方法
                mediaPlayer.prepareAsync();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param mp Mediaplayer播放完成时回调
     */
    @Override
    public void onCompletion(MediaPlayer mp) {
        statusText.setText("onComplete  called");
        //每播放完成一个条目时释放播放器
        mediaPlayer.stop();
        mediaPlayer.release();
        //查看是否还有条目,如果有向下挪一个,准备播放下一个
        if (playListItem.size() > currentPlayListItem + 1) {
            currentPlayListItem++;
            String path = ((PlayListFile) playListItem.get(currentPlayListItem)).getFilePath();
            try {
                mediaPlayer.setDataSource(path);
                mediaPlayer.prepareAsync();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
//        start.setEnabled(false);
//        stop.setEnabled(false);
    }

    /**
     * @param mp 当完成prepareAsync()状态时回调,表明音频准备播放
     */
    @Override
    public void onPrepared(MediaPlayer mp) {
        statusText.setText("prepared called");
        start.setEnabled(true);
        mediaPlayer.start();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mediaPlayer.pause();
        start.setEnabled(true);
        stop.setEnabled(false);
    }

    /**
     * @param mp
     * @param what
     * @param extra
     * @return 当MediaPalyer发生错误后调用
     */
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        statusText.setText("onError called");
        switch (what) {
            case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
                statusText.setText("MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK" + extra);
                Log.v("Error:", "MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:" + extra);
                break;
            case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
                statusText.setText("MEDIA_ERROR_SERVER_DIED:" + extra);
                Log.v("Error", "MEDIA_ERROR_SERVER_DIED:" + extra);
                break;
            case MediaPlayer.MEDIA_ERROR_UNKNOWN:
                statusText.setText("MEDIA_ERROR_UNKNOWN:" + extra);
                Log.v("Error", "MEDIA_ERROR_UNKNOWN:" + extra);
                break;
        }
        return false;
    }

    /**
     * @param mp
     * @param percent MediaPlayer正在缓冲时调用
     */
    @Override
    public void onBufferingUpdate(MediaPlayer mp, int percent) {
        bufferText.setText("" + percent + "%");
    }


    class PlayListFile {
        String filePath;

        public PlayListFile(String _filePath) {
            filePath = _filePath;
        }

        public void setFilePath(String _filePath) {
            filePath = _filePath;
        }

        public String getFilePath() {
            return filePath;
        }
    }

}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android蓝牙音频数据是指在Android设备和其他设备(如耳机、扬声器和车载音响等)之间通过蓝牙连接传输音频数据的过程。在传输过程中,音频数据从源设备(如手机或平板电脑)发送到目标设备,目标设备将音频数据转换为听到的声音。 该过程的实现需遵循蓝牙协议和Android系统API的规范。在连接设备时,首先需要配对设备并使用音频传输协议(A2DP)建立音频。然后,音频数据将在双方之间进行传输。 在传输过程中,音频数据的质量和稳定性对于用户体验至关重要。因此,要确保接收设备能够处理发送设备发送的音频格式。同时,Android设备需要足够的处理能力来支持音频数据传输和处理。 另外,Android蓝牙音频数据还可以通过各种设置来进一步优化用户体验,如增强音频效果、调整音量和平衡等。此外,配对过程中还需注意安全问题,使连接过程更加稳定和安全。 总之,Android蓝牙音频数据传输提供了用户在移动设备和其他设备之间实现高质量音频传输的便利性和稳定性。同时,需要保证音频数据的传输质量和安全性,确保用户获得最佳的音频体验。 ### 回答2: 蓝牙音频数据是将音频信号通过蓝牙协议传输到另一个设备的一种技术。Android系统支持多种蓝牙音频传输方式,包括A2DP(Advanced Audio Distribution Profile)和SCO(Synchronous Connection-Oriented)等。其中,A2DP用于音乐、视频等高质量音频传输,而SCO用于电话通话等实时音频传输。在通过蓝牙传输音频数据时,需要注意以下几个方面: 第一,蓝牙版本和协议的支持。Android系统支持多个蓝牙版本和协议,但不同的版本和协议有不同的音频传输能力和数据传输速率,需要根据实际需求选择合适的版本和协议。 第二,音频编码格式的选择。蓝牙音频传输需要将音频信号进行数字化和压缩,因此需要选择合适的编码格式。Android系统支持多种音频编码格式,包括SBC、AAC、LDAC等。其中,SBC是所有蓝牙设备都支持的标准编码格式,而AAC和LDAC等高级编码格式则需要在设备之间达成兼容。 第三,数据传输速率的优化。蓝牙数据传输速率受多种因素影响,包括信号传输距离、干扰程度、设备引入的延迟等。为了提高音频传输质量,可以通过优化连接设置、使用适当的编码格式等方式来改善数据传输速率。 总之,Android蓝牙音频数据技术的应用范围广泛,可以通过合适的设置和优化来提高传输质量和用户体验。同时,随着蓝牙技术的持续发展,未来将有更多的新型音频编码格式和协议出现,为蓝牙音频传输带来更多可能性。 ### 回答3: Android操作系统中广泛使用的蓝牙音频数据是一种基于蓝牙技术的音频传输协议。它是通过蓝牙连接将音频数据从一个设备传输到另一个设备的技术,旨在实现多种多样的蓝牙应用,如语音通话、音乐播放、耳机连接等。 一般而言,蓝牙音频数据可以分为两种类型:A2DP(Advanced Audio Distribution Profile)和HFP(Hands-Free Profile)。A2DP协议实现的是高质量音频传输,用于音乐、视频等通信场景,而HFP则实现的是语音通话功能,用于车载蓝牙调音器、蓝牙耳机等语音通讯场合。 在使用蓝牙音频数据时,需要注意的一点是数据传输的实时性,即要求音频数据传输及时性、稳定性和高速性。这就需要蓝牙设备在数据传输时保持高速稳定的连接。因此,如果连接环境较差,比如两个蓝牙设备之间物理距离较远或存在障碍物等情况,音频数据的质量可能会降低,音频传输也会出现卡顿等问题,影响用户体验。 此外,蓝牙音频数据还需要确定编码方式和传输频率。不同的编码方式和传输频率会对音频数据的传输效果产生影响。为了提高音频传输的质量,一些高端设备使用的新型编码和传输方式有所发展,比如LDAC和aptX HD等新的高清晰度传输方式。这些新技术不仅提高了音频传输质量,还为用户带来不同的音效体验,让用户可以更加舒适地享受高质量的音乐体验。 综上所述,蓝牙音频数据是一种基于蓝牙技术实现的音频传输协议,广泛应用于Android操作系统中。在使用蓝牙音频数据时,需要注意关注实时性、编码方式、传输频率等因素,以保证音频传输质量和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值