最近一直在研究直播功能,播放画面和播放音频是大头,弄了很久一直没弄好,视频播放这边主要是,使用HLS功能实现的播放功能。
在这里主要说下音频流直播的实现,在和同事不断的努力和交流下,终于播放出了声音,万份惊喜啊。有种屌丝逆袭的感觉。好的不扯那么多了进入正题:
首先,是基于大牛破解国外的juv之后,继续实现的连接red5服务器的功能;
连接的代码,这边是在主activity里面写的:
public class MainActivity extends Activity { // fields private Button btnStart, finish; String url = "you red5 server"; String externUID = "userId"; String username = "userName"; private Object callId = "75050";//回掉时服务器提供的 final UltraNetConnection connection = new UltraNetConnection(); private UltraNetStream stream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); connection.addEventListener(new NetConnectionListener()); connection.client(MainActivity.this); btnStart = (Button) findViewById(R.id.btnStart); btnStart.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { start(); } }); finish = (Button) findViewById(R.id.finish); finish.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (stream != null) { stream.close(); } MainActivity.this.finish(); } }); } /** * 1 、Starts the example. */ public void start() { new AsyncTask<Void, Void, Boolean>() { @Override protected Boolean doInBackground(Void... params) { connection.connect(url, externUID, username); // connection.connect(url); // 1 wait till connected while (!connection.connected() && !disconnected) { try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } if (!disconnected) { stream = new UltraNetStream(connection); stream.addEventListener(new NetStream.ListenerAdapter() { @Override public void onNetStatus(final INetStream source, final Map<String, Object> info) { System.out .println("NetStream#onNetStatus: " + info); } }); } while (!disconnected) { try { Thread.sleep(100); } catch (Exception e) {/* ignore */ Log.e("ThreadException", "连接失败\n\n" + e.toString()); } } connection.close(); return null; }; }.execute((Void) null); } /** * 2 、 <code>NetConnectionListener</code> - {@link UltraNetConnection} * listener implementation. */ public class NetConnectionListener extends NetConnection.ListenerAdapter { /** * Constructor. */ public NetConnectionListener() { } @Override public void onAsyncError(final INetConnection source, final String message, final Exception e) { System.out.println("NetConnection#onAsyncError: " + message + " " + e); } @Override public void onIOError(final INetConnection source, final String message) { System.out.println("NetConnection#onIOError: " + message); } @Override public void onNetStatus(final INetConnection source, final Map<String, Object> info) { // 2 System.out.println("NetConnection#onNetStatus: " + info); final Object code = info.get("code"); if (NetConnection.CONNECT_SUCCESS.equals(code)) { connection.call("voiceconf.call", null, "default", username, callId); } else { disconnected = true; } } } // 3、 public void successfullyJoinedVoiceConferenceCallback(String publishName, String playName, String codec) { System.out.println("publishName:" + publishName + "\n\nplayName:" + playName + "\n\ncodec:" + codec); try { // stream.receiveAudio(true); // stream.receiveVideo(false); if (stream == null) { stream = new UltraNetStream(connection); } //调用该方法可以传递直播音频流,playname是连接成功后,red5返回给的speex音频名称 stream.play(new FlvVideo(playName + ".flv"), playName); } catch (Exception e) { e.printStackTrace(); } } // fields private volatile boolean disconnected = false; public void getSystemOfflineMsg(String str) { System.out.println(str); } public void getSystemMSG(String str) { System.out.println(str); } public void updatePublicInfo(String str) { System.out.println(str); } public void newMessageShow() { System.out.println("新信件"); } public void newUpdateSocialityInfo() { System.out.println("新pk"); } public void updateAutoUndercityInfo(String str) { System.out.println("自动闯关完成" + str); } public int onBWCheck(Object o) { return 0; } public int onBWCheck() { return 0; } public void onBWDone(Object[] paramArrayOfObject) { System.out.println("test onBWDone"); } public static void create_newRoom(String str) { System.out.println("\n\n\nserverbackof_room --------: [" + str + "]\n\n\n"); } public static void callJS_back_app(String str) { System.out.println("\n\n\nserverbackof_app --------: [" + str + "]\n\n\n"); } }
这个方法是连接服务器的,其中用到的两个UltraNetConnection 和 UltraNetStream 两个类的代码如下:
package com.weedong.net.rtmp; import java.io.File; import java.lang.reflect.Method; import java.util.Calendar; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import com.smaxe.logger.ILogger; import com.smaxe.uv.ProtocolLayerInfo; import com.smaxe.uv.Responder; import com.smaxe.uv.UrlInfo; import com.smaxe.uv.client.INetConnection; import com.smaxe.uv.client.a.d; import com.smaxe.uv.client.a.h; import com.smaxe.uv.client.a.i; import com.smaxe.uv.client.a.k; public final class UltraNetConnection extends i implements INetConnection { private final h a; private d b = null; private a c = null; private ExecutorService d = null; private ScheduledExecutorService e = null; private boolean f = false; private boolean g = false; private static final int h = 19; private static final int i = 9; private static final int[] j = { 5, 2, 7, 1, 0, 3, 6, 4 }; private static byte[] k = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; public static void setSwfFileSizeAndHash(Map<String, Object> paramMap, File paramFile) throws Exception { a(paramMap, paramFile); } public UltraNetConnection() { this(null); } public UltraNetConnection(Map<String, Object> paramMap) { this(paramMap, null, null); } public UltraNetConnection(Map<String, Object> paramMap, ExecutorService paramExecutorService, ScheduledExecutorService paramScheduledExecutorService) { super(paramMap); this.d = (paramExecutorService == null ? Executors.newCachedThreadPool() : paramExecutorService); this.e = (paramScheduledExecutorService == null ? Executors.newSingleThreadScheduledExecutor() : paramScheduledExecutorService); this.f = (paramExecutorService == null); this.g = (paramScheduledExecutorService == null); this.a = new k(this.e); } public void addHeader(String paramString, boolean paramBoolean, Object paramObject) { this.b.a(paramString, paramBoolean, paramObject); } public void call(String paramString, Responder paramResponder, Object[] paramArrayOfObject) { if (!connected()) return; this.b.a(paramString, paramResponder, paramArrayOfObject); } public void close() { b(com.smaxe.uv.a.e.b("NetConnection.Connect.Closed", "Connection is closed.")); } public void connect(String paramString, Object... paramArrayOfObject) { // b(k); UrlInfo localUrlInfo = UrlInfo.parseUrl(paramString); com.smaxe.uv.client.a.e locale = new com.smaxe.uv.client.a.e(); locale.a(this.d); locale.a((ILogger)configuration().get("logger")); this.b = locale; this.c = new a(); try { Method bitchMethod = com.smaxe.uv.client.a.h.class.getMethod("a", String.class, String.class, int.class, Map.class); Object btichResult = bitchMethod.invoke(this.a, localUrlInfo.protocol, localUrlInfo.host, localUrlInfo.port, configuration()); Method[] aryMethod = com.smaxe.uv.client.a.d.class.getMethods(); for(Method method : aryMethod) { if(method.getName().equals("a") && method.getParameterTypes().length == 6) { method.invoke(this.b, this, btichResult, paramString, localUrlInfo.getApp(), this.c, paramArrayOfObject); break; } } } catch(Exception ex) { ex.printStackTrace(); } super.connect(paramString, paramArrayOfObject); } public boolean connected() { if (this.b == null) return false; return this.b.a() == 3; } public String connectedProxyType() { return connected() ? this.b.b() : null; } public boolean usingTLS() { return connected() ? this.b.c() : false; } public ProtocolLayerInfo getInfo() { return this.b.d(); } public int getUploadBufferSize() { return this.b.e(); } public void setMaxUploadBandwidth(int paramInt) { if (paramInt < 0) throw new IllegalArgumentException("Parameter 'bandwidth' is negative: " + paramInt); this.b.a(paramInt); } public void onBWDone() { } public void onBWDone(Object[] paramArrayOfObject) { } private void b(Map<String, Object> paramMap) { if (this.b == null) return; this.b.a(paramMap); if ((this.f) && (this.d != null)) this.d.shutdown(); if ((this.g) && (this.e != null)) this.e.shutdown(); this.d = null; this.e = null; } static void a(byte[] paramArrayOfByte) { if ((paramArrayOfByte == null) || (paramArrayOfByte.length != 25)) return; k = paramArrayOfByte; } d a() { return this.b; } private static void b(byte abyte0[]) throws IllegalArgumentException { int l = 0; for(int i1 = 1; i1 < abyte0.length - 1; i1++) l += abyte0[i1] & 0xff; l &= 0xff; int j1 = abyte0[1] & 0xf; if((abyte0[0] & 0xff) != (byte)(l >> 0 & 0xf) || (abyte0[abyte0.length - 1] & 0xff) != (byte)(l >> 4 & 0xf) || abyte0[1] + abyte0[abyte0.length - 2] != 15) a(16); boolean aflag[] = new boolean[21]; byte abyte1[] = new byte[8]; int k1 = 1; int l1 = j1; for(int i2 = 0; i2 < abyte1.length; i2++) { for(; aflag[l1 % aflag.length]; l1++); aflag[l1 % aflag.length] = true; abyte1[i2] = abyte0[2 + l1 % aflag.length]; k1 += 2; l1 += k1; } if((abyte1[1] & 0xf) != 3) a(32); boolean flag = (abyte1[3] & 0xf) >= 8; int j2 = (flag ? abyte1[3] - 8 : abyte1[3]) & 0xf; if(j2 < 1) a(1); if(flag) { Calendar calendar = Calendar.getInstance(); calendar.set(1, 2000 + (abyte1[4] & 0xf)); calendar.set(2, (abyte1[5] & 0xf) - 1); calendar.set(5, ((abyte1[6] & 0xf) << 4) + (abyte1[7] & 0xf)); if(System.currentTimeMillis() - calendar.getTimeInMillis() > 0L) a(18); } } private static void a(int paramInt) { switch (paramInt & 0xF) { case 0: throw new IllegalArgumentException(a(new long[] { 8460391658548064800L, 8315163859177334048L, 8319872964449869929L, 7205878151055483136L })); case 1: throw new IllegalArgumentException(a(new long[] { 8460391658548064800L, 8315163859177334048L, 8319309735340351598L, 7811060823377406308L, 7162256601089340786L, 8532478991051810162L, 120946281218048L })); case 2: throw new IllegalArgumentException(a(new long[] { 8462924959242482208L, 2314957309810076517L, 2335505025909089656L, 2378011653932580864L })); } } private static String a(long[] paramArrayOfLong) { byte[] arrayOfByte = new byte[paramArrayOfLong.length * 8]; int m = 0; for (int n = 0; n < paramArrayOfLong.length; n++) for (int i1 = 0; i1 < 8; i1++) { byte i2 = (byte)(int)(paramArrayOfLong[n] >> j[i1] * 8 & 0xFF); if (i2 == 0) break; arrayOfByte[(n * 8 + i1)] = i2; m++; } return new String(arrayOfByte, 0, m); } static void a(UltraNetConnection netconnection, String s, Exception exception) { netconnection.a(s, exception); } static void a(UltraNetConnection netconnection, String s) { netconnection.a(s); } static void a(UltraNetConnection netconnection, Map map) { netconnection.b(map); } static void b(UltraNetConnection netconnection, Map map) { netconnection.a(map); } static void c(UltraNetConnection netconnection, Map map) { netconnection.a(map); } private class a extends d.a { public a() { } public void a(String paramString, Exception paramException) { UltraNetConnection.a(UltraNetConnection.this, paramString, paramException); } public void a(String paramString) { UltraNetConnection.a(UltraNetConnection.this, paramString); } public void a(Map<String, Object> paramMap) { String str = (String)paramMap.get("code"); if ((!"NetConnection.Connect.Success".equals(str)) && (!"NetConnection.Connect.Bandwidth".equals(str)) && (!"NetConnection.Call.Failed".equals(str))) UltraNetConnection.a(UltraNetConnection.this, paramMap); UltraNetConnection.b(UltraNetConnection.this, paramMap); } public void a(long paramLong1, long paramLong2) { if (!((Boolean)UltraNetConnection.this.configuration().get("enableAcknowledgementEventNotification")).booleanValue()) return; Map localMap = com.smaxe.uv.a.e.b("NetConnection.Connect.Bandwidth", "'Acknowledgement' event notification."); localMap.put("acknowledgement", Long.valueOf(paramLong1)); localMap.put("info", new ProtocolLayerInfo(UltraNetConnection.this.getInfo())); localMap.put("uploadBufferSize", Long.valueOf(paramLong2)); UltraNetConnection.c(UltraNetConnection.this, localMap); } } }
package com.weedong.net.rtmp; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Map; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.util.Log; import com.gauss.speex.encode.Speex; import com.smaxe.uv.client.ICamera; import com.smaxe.uv.client.IMicrophone; import com.smaxe.uv.client.INetConnection; import com.smaxe.uv.client.INetStream; import com.smaxe.uv.client.IVideo; import com.smaxe.uv.client.a.c; import com.smaxe.uv.client.video.EmptyVideo; import com.smaxe.uv.stream.MediaData; public final class UltraNetStream extends c implements INetStream { private com.smaxe.uv.client.a.d videoControl = null; private IVideo ivideo = new EmptyVideo(); private volatile boolean c = false; private IMicrophone d = null; private ICamera e = null; private e f = null; private c g = null; private VideoOrAudioInfo audioData = null; private VideoOrAudioInfo videoData = null; public UltraNetStream(INetConnection paramINetConnection) { this.videoControl = ((UltraNetConnection) paramINetConnection).a(); this.videoControl.a(paramINetConnection, this, new d()); this.audioData = new VideoOrAudioInfo(); this.videoData = new VideoOrAudioInfo(); } public INetStream.Info getInfo() { // return video/audio frame;size; System.out.println("h.a = " + this.audioData.duration); System.out.println("h.b = " + this.audioData.frames); System.out.println("h.c = " + this.audioData.size); return new INetStream.Info(this.audioData.duration, this.audioData.frames, this.audioData.size, this.videoData.duration, this.videoData.frames, this.videoData.size); } public ICamera getCamera() { return this.e; } public IMicrophone getMicrophone() { return this.d; } public IVideo getVideo() { if ((this.ivideo instanceof MyVideo)) return ((MyVideo) this.ivideo).a(); return this.ivideo; } public double bufferLength() { return this.c ? this.videoControl.b(this) : this.ivideo.bufferLength(); } public int bufferSize() { return this.c ? this.videoControl.c(this) : 0; } public long bytesLoaded() { return this.ivideo.bytesLoaded(); } public long bytesTotal() { return this.ivideo.bytesTotal(); } public int clearBuffer() { return this.videoControl.a(this); } public void attachAudio(IMicrophone paramIMicrophone) { if (this.d == paramIMicrophone) return; if (this.d != null) { this.d.removeListener(this.g); this.g = null; } this.d = paramIMicrophone; if (this.d != null) this.d.addListener(this.g = new c()); } public void attachCamera(ICamera paramICamera, int paramInt) { if (this.e == paramICamera) return; if (this.e != null) { this.e.removeListener(this.f); this.f = null; } this.e = paramICamera; if (this.e != null) this.e.addListener(this.f = new e(paramInt)); } public void publish(String paramString1, String paramString2) { paramString2 = paramString2 == null ? "live" : paramString2; if ((!"append".equalsIgnoreCase(paramString2)) && (!"live".equalsIgnoreCase(paramString2)) && (!"record".equalsIgnoreCase(paramString2))) throw new IllegalArgumentException("Wrong publish type '" + paramString2 + "'. Use 'live','record' or 'append' type"); this.videoControl.a(this, paramString1, paramString2); this.c = true; } public void send(String paramString, Object... paramArrayOfObject) { this.videoControl.a(this, paramString, paramArrayOfObject); } public double currentFPS() { return this.ivideo.fps(); } public double liveDelay() { return this.ivideo.liveDelay(); } public double time() { return this.ivideo.time(); } public void play(IVideo paramIVideo, Object... paramArrayOfObject) { if (paramIVideo == null) throw new IllegalArgumentException("Parameter 'video' is null"); this.ivideo = new MyVideo(paramIVideo); this.videoControl.a(this, this.ivideo, paramArrayOfObject); } public void pause() { this.videoControl.d(this); } public void receiveAudio(boolean paramBoolean) { this.videoControl.a(this, paramBoolean); } public void receiveVideo(boolean paramBoolean) { this.videoControl.b(this, paramBoolean); } public void resume() { this.videoControl.e(this); } public void seek(double paramDouble) { this.videoControl.a(this, paramDouble); } public void togglePause() { if (this.c) pause(); else resume(); } public void close() { attachAudio(null); attachCamera(null, 0); this.videoControl.f(this); super.close(); } public void _RtmpSampleAccess(boolean paramBoolean1, boolean paramBoolean2) { } static void a(UltraNetStream netstream, String s, Exception exception) { netstream.a(s, exception); } static void a(UltraNetStream netstream, String s) { netstream.a(s); } static void a(UltraNetStream netstream, Map map) { netstream.c(map); } static void b(UltraNetStream netstream, Map map) { netstream.a(map); } static IVideo a(UltraNetStream netstream) { return netstream.ivideo; } static void c(UltraNetStream netstream, Map map) { netstream.b(map); } static void d(UltraNetStream netstream, Map map) { netstream.d(map); } static boolean b(UltraNetStream netstream) { return netstream.c; } static VideoOrAudioInfo c(UltraNetStream netstream) { return netstream.videoData; } static com.smaxe.uv.client.a.d d(UltraNetStream netstream) { return netstream.videoControl; } // TASK System callback method static VideoOrAudioInfo e(UltraNetStream netstream) { System.out.println("netstream.getVideo:" + netstream.getInfo()); return netstream.audioData; } private final class VideoOrAudioInfo { public int duration = 0; public int frames = 0; public int size = 0; // File file = new File("//mnt//sdcard//rtmpStream.txt"); // OutputStream os = null; // private FileOutputStream fos; byte[] b; private Speex speexDecoder; private AudioTrack track; private int minBufferSize; int decsize = 0; short[] decoded = new short[160]; public VideoOrAudioInfo() { speexDecoder = new Speex(); speexDecoder.init(); minBufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM); } // TODO MediaData public MediaData a(MediaData paramMediaData) { this.duration = (this.frames == 0 ? 0 : this.duration + paramMediaData.rtime); this.frames += 1; this.size += paramMediaData.size(); try { InputStream mdate = paramMediaData.read(); int dataLen = mdate.available() - 1; // Log.d("audioSize", dataLen+""); b = new byte[dataLen]; mdate.read(b, 0, 1); mdate.read(b, 0, dataLen); if (dataLen > 11) { if ((decsize = speexDecoder.decode(b, decoded, dataLen)) > 0) { track.write(decoded, 0, decsize); track.setStereoVolume(0.7f, 0.7f); track.play(); } } } catch (IOException e) { Log.e("IOException", e.toString()); } return paramMediaData; } } private final class MyVideo implements IVideo { private final IVideo v; public MyVideo(IVideo arg2) { this.v = arg2; } public IVideo a() { return this.v; } public double bufferLength() { return this.v.bufferLength(); } public long bytesLoaded() { return this.v.bytesLoaded(); } public long bytesTotal() { return this.v.bytesTotal(); } public void clear() { this.v.clear(); } public void clearPlayBuffer() { this.v.clearPlayBuffer(); } public double fps() { return this.v.fps(); } public double liveDelay() { return this.v.liveDelay(); } public void reset() { this.v.reset(); } public double time() { return this.v.time(); } public void onAudioData(MediaData paramMediaData) { this.v.onAudioData(UltraNetStream.e(UltraNetStream.this).a( paramMediaData)); System.out.println("" + paramMediaData); } public void onVideoData(MediaData paramMediaData) { this.v.onVideoData(UltraNetStream.c(UltraNetStream.this).a( paramMediaData)); } public void onFlvData(MediaData paramMediaData) { this.v.onFlvData(paramMediaData); } public void onCuePoint(Object paramObject) { this.v.onCuePoint(paramObject); } public void onMetaData(Object paramObject) { this.v.onMetaData(paramObject); } public void onSetDataFrame(String paramString, Object paramObject) { this.v.onSetDataFrame(paramString, paramObject); } } private final class c extends IMicrophone.ListenerAdapter { public c() { } public void onAudioData(MediaData paramMediaData) { if (!UltraNetStream.b(UltraNetStream.this)) return; UltraNetStream.d(UltraNetStream.this).a(UltraNetStream.this, UltraNetStream.e(UltraNetStream.this).a(paramMediaData)); } } private final class e extends ICamera.ListenerAdapter { private final int b; private long c = 0L; public e(int arg2) { this.b = arg2; } public void onVideoData(MediaData paramMediaData) { if (!UltraNetStream.b(UltraNetStream.this)) return; if (this.b > 0) { try { Class<?> clzF = Class.forName("com.smaxe.uv.a.a.f"); Method m = clzF.getMethod("a", int.class); long l = System.currentTimeMillis(); if (l - this.c < this.b) return; int value = (Integer) m.invoke(clzF, paramMediaData.tag()); switch (value) { case 1: case 5: this.c = l; break; default: return; } } catch (Exception ex) { ex.printStackTrace(); } } UltraNetStream.d(UltraNetStream.this).b(UltraNetStream.this, UltraNetStream.c(UltraNetStream.this).a(paramMediaData)); } public void onFlvData(MediaData paramMediaData) { if (!UltraNetStream.b(UltraNetStream.this)) return; UltraNetStream.d(UltraNetStream.this).c(UltraNetStream.this, paramMediaData); } } private class d extends com.smaxe.uv.client.a.d.a { public d() { } public void a(String paramString, Exception paramException) { UltraNetStream.a(UltraNetStream.this, paramString, paramException); } public void a(String paramString) { UltraNetStream.a(UltraNetStream.this, paramString); } public void a(Map<String, Object> paramMap) { UltraNetStream.a(UltraNetStream.this, paramMap); } public void b(Map<String, Object> paramMap) { UltraNetStream.b(UltraNetStream.this, paramMap); UltraNetStream.a(UltraNetStream.this).onCuePoint(paramMap); } public void c(Map<String, Object> paramMap) { UltraNetStream.c(UltraNetStream.this, paramMap); UltraNetStream.a(UltraNetStream.this).onMetaData(paramMap); } public void d(Map<String, Object> paramMap) { UltraNetStream.d(UltraNetStream.this, paramMap); } public void a(String paramString, Map<String, Object> paramMap) { UltraNetStream.a(UltraNetStream.this).onSetDataFrame(paramString, paramMap); } } private final static byte[] hex = "0123456789ABCDEF".getBytes(); public static String Bytes2HexString(byte[] b) { byte[] buff = new byte[2 * b.length]; for (int i = 0; i < b.length; i++) { buff[2 * i] = hex[(b[i] >> 4) & 0x0f]; buff[2 * i + 1] = hex[b[i] & 0x0f]; } return new String(buff); } }
以上两个是辅助的被破解的包,其中 UltraNetStream 中的 VideoOrAudioInfo 类是不断的服务器回掉的类,里面有个方法:
// TODO MediaData public MediaData a(MediaData paramMediaData) { this.duration = (this.frames == 0 ? 0 : this.duration + paramMediaData.rtime); this.frames += 1; this.size += paramMediaData.size(); try { InputStream mdate = paramMediaData.read(); int dataLen = mdate.available() - 1; // Log.d("audioSize", dataLen+""); b = new byte[dataLen]; mdate.read(b, 0, 1); mdate.read(b, 0, dataLen); if (dataLen > 11) { if ((decsize = speexDecoder.decode(b, decoded, dataLen)) > 0) { track.write(decoded, 0, decsize); track.setStereoVolume(0.7f, 0.7f); track.play(); } } } catch (IOException e) { Log.e("IOException", e.toString()); } return paramMediaData; }
该方法是red5不断一直回掉的函数,在这个函数里, paramMediaData 是我们一直要的音频流,这里我将它转成了ImputStream ,并且交给speex进行decode,decode完成后,交给手机的声卡进行播放;在不断的调试中,发现 paramMediaData 这个每次传递过来的都是43位数据,通过抓包,输出log发现,
每一行开头都会有个B2,查阅另一位大神(http://blog.csdn.net/cssmhyl/article/details/8128705)的帖子,发现B2为speex音频格式的数据头,播放音频时需要去除该音频头信息,
于是就使用该方法,调试,
<span style="white-space:pre"> </span>int dataLen = mdate.available() - 1; // Log.d("audioSize", dataLen+""); b = new byte[dataLen]; mdate.read(b, 0, 1); mdate.read(b, 0, dataLen);将头位置不放到b里面,直接从B2后面的位置开始读取数据;不然的话,会一直听见各种杂音;这样就可以播放正常的声音了;
如果碰见声音音调不正常的,估计是采样率设置的问题,我使用的是8kHz,声音播放正常了;
这边需要注意的是,VideoOrAudioInfo(){}这个构造函数中,需要初始化speex,一个minBufferSize 和一个 声卡对象;