juv rtmp client android speex音频解析 播放red5的音频流实现

时间:2022-02-24 04:00:31

最近一直在研究直播功能,播放画面和播放音频是大头,弄了很久一直没弄好,视频播放这边主要是,使用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发现,

juv rtmp client android speex音频解析 播放red5的音频流实现

每一行开头都会有个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 和一个 声卡对象;