android开发:把⼀个byte数组转换成wav⾳频⽂件,并且播放随着发布MP3⽂件、播客以及流式⾳频变得越来越受欢迎,构建可以利⽤这些服务的⾳频播放程序的需求也越来越强烈。幸运的
是,Android拥有丰富的功能⽤于处理⽹络上存在的各种类型的⾳频。
1.基于HTTP⾳频播放
但是这⾥和通常⽰例化MediaPlayer的⽅式不同,⾸先使⽤的是MediaPlayer的⽆参构造函数来实例化对象,接着,调⽤其setDataSource ⽅法,传⼊想要播放的⾳频的HTTP位置,随后我们调⽤prepare⽅法和start⽅法。
mediaPlayer = new MediaPlayer();
try {
mediaPlayer
.setDataSource("bvcasting/android/audio/goodmorningandroid.mp3");
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
Log.v("AUDIOHTTPPLAYER", e.getMessage());
}
但是,在应⽤程序加载到播放⾳频之间有⼀个明显的滞后时间。延迟的长度取决于⽤于构建电话Internet连接的数据⽹络的速度。如果详细分析的话,可以到是在调⽤prepare⽅法和start⽅法之间发⽣了这样的延迟。在运⾏prepare期间,MediaPlayer将填充⼀个缓冲区,因为即使⽹络速度缓慢也能平稳的播放⾳频。当这么操作时,prepare⽅法实际上发⽣了阻塞。这意味着应⽤程序可能要等到prepare⽅法完成之后才会响应。幸运的是,有⼀种⽅法可以解决这个问题,即prepareAsync⽅法。该⽅法会⽴即返回,并在后台执⾏缓冲和其他⼯作,从⽽允许应⽤程序继续运⾏。
完整⽰例代码如下:
public class AudioHTTPPlayer extends Activity implements OnClickListener,
OnErrorListener, OnCompletionListener, OnBufferingUpdateListener,
OnPreparedListener
{
/** Called when the activity is first created. */
MediaPlayer mediaPlayer;
Button stopButton, startButton;
TextView statusTextView, bufferValueTextView;
@Override
public void onCreate(Bundle savedInstanceState)
{
setContentView(R.layout.main);
stopButton = (Button) findViewById(R.id.EndButton);
startButton = (Button) findViewById(R.id.StartButton);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
startButton.setEnabled(false);
stopButton.setEnabled(false);
bufferValueTextView = (TextView) findViewById(R.id.BufferValueTextView);
statusTextView = (TextView) findViewById(R.id.StatusDisplayTextView);
statusTextView.setText("onCreate");
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
statusTextView.setText("MediaPlayer created");
try
{
mediaPlayer
.setDataSource("bvcasting/android/audio/goodmorningandroid.mp3");
// mediaPlayer.prepare();
// mediaPlayer.start();
statusTextView.setText("setDataSource done");
statusTextView.setText("calling prepareAsync");
mediaPlayer.prepareAsync();// 开始在后台缓冲⾳频⽂件并返回
} catch (IOException e)
{
Log.v("AUDIOHTTPPLAYER", e.getMessage());
}
}
@Override
public void onPrepared(MediaPlayer mp)
{
/
/ TODO Auto-generated method stub
// 当完成prepareAsync⽅法时,将调⽤活动的onPrepared⽅法
statusTextView.setText("onPrepared called");
startButton.setEnabled(true);
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent)
{
// TODO Auto-generated method stub
// 当MediaPlayer正在缓冲时,将调⽤活动的onBufferingUpdate⽅法
bufferValueTextView.setText(""+percent+"%");
}
@Override
public void onCompletion(MediaPlayer mp)
{
// TODO Auto-generated method stub
statusTextView.setText("onCompletion called");
stopButton.setEnabled(false);
startButton.setEnabled(true);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra)
{
// TODO Auto-generated method stub
switch (what)
{
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
statusTextView
.setText("MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK"
+ extra);
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
statusTextView.setText("MEDIA_ERROR_SERVER_DIED" + extra);
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
statusTextView.setText("MEDIA_ERROR_UNKNOWN" + extra);
break;
}
return false;
}
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
if (v == stopButton)
{
mediaPlayer.pause();
statusTextView.setText("pause called");
startButton.setEnabled(true);
} else if (v == startButton)
{
mediaPlayer.start();
statusTextView.setText("start called");
startButton.setEnabled(false);
stopButton.setEnabled(true);
}
}
}
如上所⽰,MediaPlayer有良好的功能集,⽤来处理HTTP在线获取的⾳频⽂件。
2.基于HTTP的流式⾳频
在线⾳频常⽤的在线传输⽅法之⼀是通过HTTP流。有多种流⽅法属于HTTP流⽅法的分⽀,包括服务器推送,这在历史上⼀直⽤于在浏览器中刷新⽹络摄像头图像显⽰;以及⼀系列其他新⽅法。⽽联机⼴播事实上的标准则是ICY协议,其扩展了HTTP协议,⽬前⼤量的服务器和播放软件产品都⽀持这个协议。
幸运的是,android上的MediaPlayer⽀持播放ICY流,⽽⽆须开发⼈员费⼒地实现它。
然后,Internet⼴播电台并不直接公布它们的⾳频流的URL。这么做是因为浏览器通常不⽀持ICY流,⽽是需要⼀个辅助应⽤程序或插件来播放流。为了知道要打开的是⼀个辅助应⽤程序,Internet⼴播电台会 传递⼀个特定的MIME类型的中间⽂件,其中包含⼀个指向实际在线流的指针。在使⽤ICY流的情况下,这通常是⼀个PLS⽂件或⼀个M3U⽂件
PLS⽂件:是⼀种多媒体播放列表⽂件,其MIME类型是“audio/x-scpls”
M3U⽂件:⼀个存储多媒体播放列表的⽂件,但是采⽤⼀种更基本的格式。它的MIME类型为“audio/x-mpegurl”。
例如M3U⽂件的内容如下,其指向了⼀个虚假的在线流
#EXTM3U
#EXTINF:0,Live Stream Name
:8000/
第⼀⾏的#EXTM3U是必须的,其指定下⾯是⼀个扩展的M3U⽂件,其中可以包含额外的信息。可以在播放列表条⽬的上⼀⾏指定额外信息,其以#EXTINF:开始,随后是以秒为单位的持续时间和逗号,然后是媒体的名称。
M3U⽂件可以同时包含多个条⽬,这些条⽬依次指定⼀个⽂件或流
#EXTM3U
#EXTINF:0,Live Stream Name
:8000/
#EXTINF:0,Other Live Stream Name
/
遗憾的是,android上的MediaPlayer不能⾃动分析M3U⽂件。因此必须我们⾃⼰分析。下⾯就是⼀个⽰例,分析并播放来⾃联机⼴播电台的M3U⽂件或在URL字段中输⼊的任何M3U⽂件。
public class HTTPAudioPlaylistPlayer extends Activity implements
OnClickListener, OnCompletionListener, OnPreparedListener
{
Vector playlistItems;
Vector playlistItems;
Button parseBtn, playBtn, stopBtn;
EditText editTextUrl;
String baseURL = "";
MediaPlayer mediaPlayer;
int currentPlaylistItemNumber = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
setContentView(R.layout.main2);
parseBtn = (Button) findViewById(R.id.ParseButton); playBtn = (Button) findViewById(R.id.PlayButton);
stopBtn = (Button) findViewById(R.id.StopButton);
editTextUrl=(EditText) findViewById(R.id.EditTextURL);
playBtn.setOnClickListener(this);
parseBtn.setOnClickListener(this);
stopBtn.setOnClickListener(this);
playBtn.setEnabled(false);
stopBtn.setEnabled(false);
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnPreparedListener(this);
}
@Override
public void onPrepared(MediaPlayer mp)
{
// TODO Auto-generated method stub
stopBtn.setEnabled(true);
Log.v("HTTPAUDIOPLAYLIST", "Playing");
mediaPlayer.start();
}
@Override
public void onCompletion(MediaPlayer mp)
{
// TODO Auto-generated method stub
Log.v("ONCOMPLETION", "called");
mediaPlayer.stop();
if (playlistItems.size() > currentPlaylistItemNumber + 1) {
currentPlaylistItemNumber++;
String path = ((PlaylistFile) playlistItems
.get(currentPlaylistItemNumber)).getFilePath();
try
{
mediaPlayer.setDataSource(path);
mediaPlayer.prepareAsync();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
} catch (IllegalStateException e)
{
e.printStackTrace();
} catch (IOException e)
{
{
e.printStackTrace();
}
}
}
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
if (v == parseBtn)
{
// 下载由editTextUrl对象中的URL指定的M3U⽂件,并对它进⾏分析。
// 分析的操作是选出任何表⽰待播放⽂件的⾏,创建⼀个PlaylistItem对象, // 然后把它添加到playlistItems容器⾥
parsePlaylistFile();
} else if (v == playBtn)
{
playPlaylistItems();
} else if (v == stopBtn)
{
stop();
}
}
private void parsePlaylistFile()
{
// TODO Auto-generated method stub
playlistItems = new Vector();
// 为了从Web获取M3U⽂件,可以使⽤Apache软件基⾦会的HttpClient库, // 它已被android所包括。
// ⾸先创建⼀个HttpClient对象,其代表类似Web浏览器的事物;
HttpClient httpClient = new DefaultHttpClient();
// 然后创建⼀个HttpGet对象,其表⽰指向⼀个⽂件的具体请求。
HttpGet getRequest = new Text().toString());
Log.v("URI", URI().toString());
// HttpClient将执⾏HttpGet,并返回⼀个HttpResponse
try
{
HttpResponse httpResponse = ute(getRequest);
if (StatusLine().getStatusCode() != HttpStatus.SC_OK) {
Log.v("HTTP ERROR", StatusLine()
.getReasonPhrase());
} else
{
// 在发出请求之后,可以从HttpRequest中获取⼀个InputStream,
// 其包含了所请求⽂件的内容
InputStream inputStream = Entity().getContent();
// 借助⼀个BufferedReader可以逐⾏得遍历该⽂件
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(inputStream));
String line;
while ((line = adLine()) != null)
{
Log.v("PLAYLISTLINE", "ORIG:" + line);
if (line.startsWith("#"))
{
// 元数据,可以做更多的处理,但现在忽略它
} else if (line.length() > 0)
{
// 如果它的长度⼤于0,那么就假设它是⼀个播放列表条⽬
String filePath = "";
if (line.startsWith(""))
error parse new{
// 如果⾏以“”开头那么就把它作为流的完整URL
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论