Android使用MediaPlayer播放视频步骤

MediaPlayer状态变换图:

alt none

图片来自Google Android Doc

public class MainActivity extends Activity {

	Display display;
	SurfaceHolder surfaceHolder;
	SurfaceView surfaceView;
	MediaPlayer mediaPlayer;
	// 视频宽高
	int width = 0;
	int height = 0;

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

		display = getWindowManager().getDefaultDisplay();

		String file = Environment.getExternalStorageDirectory()
				.getAbsolutePath() + "/Movies/1.m4v";

		surfaceView = (SurfaceView) findViewById(R.id.sv);
		surfaceHolder = surfaceView.getHolder();

		surfaceHolder.addCallback(new SurfaceHolder.Callback() {

			// 当SurfaceView销毁时调用
			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
				// TODO Auto-generated method stub
			}

			@Override
			public void surfaceCreated(SurfaceHolder holder) {
                                // MediaPlayer通过SurfaceHolder与SurfaceView联系在一起
				mediaPlayer.setDisplay(holder);
			}

			// 当SurfaceView大小发生变化时调用
			@Override
			public void surfaceChanged(SurfaceHolder holder, int format,
					int width, int height) {
				// TODO Auto-generated method stub
			}
		});

		mediaPlayer = new MediaPlayer();

		mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

					@Override
					public void onCompletion(MediaPlayer mp) {
						// TODO Auto-generated method stub

					}
				});

		mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

					@Override
					public void onCompletion(MediaPlayer mp) {
						Log.v("MediaPlayer", "play completion");
					}
				});

		mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {

			@Override
			public boolean onError(MediaPlayer mp, int what, int extra) {

				if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
					Log.v("MediaPlayer", "MediaPlayer Server died");
				} else if (what == MediaPlayer.MEDIA_ERROR_UNKNOWN) {
					Log.v("MediaPlayer", "Unknown error in MediaPlayer: "
							+ extra);
				}

				// 这里返回false表明没有处理捕捉到的错误,将交由上层来处理。如果mediaPlayer注册了OnCompletionListener,系统将会调用其onCompletion方法
				return false;
			}
		});

		mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {

			@Override
			public void onSeekComplete(MediaPlayer mp) {
				Log.v("MediaPlayer", "MediaPlayer seek complete");
			}
		});

		mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {

			@Override
			public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
				Log.v("MediaPlayer", "VideoSizeChanged: " + String.valueOf(width) + "x" + String.valueOf(height));
			}
		});

		mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

			@Override
			public void onPrepared(MediaPlayer mp) {

				// 获取视频大小
				width = mp.getVideoWidth();
				height = mp.getVideoHeight();

				// 对视频宽高进行缩放
				if (width > display.getWidth() || height > display.getHeight()) {
					float heightRatio = (float) height / display.getHeight();
					float widthRatio = (float) width / display.getWidth();

					if (heightRatio > 1 || widthRatio > 1) {

						if (heightRatio > widthRatio) {
							height = (int) Math.ceil(height / heightRatio);
							width = (int) Math.ceil(width / heightRatio);
						} else {
							height = (int) Math.ceil(height / widthRatio);
							width = (int) Math.ceil(width / widthRatio);
						}
					}
				}

				// 设置SurfaceView的大小
				surfaceView.setLayoutParams(new LinearLayout.LayoutParams(
						width, height));

				//开始播放
				mp.start();
			}
		});

		try {
			mediaPlayer.setDataSource(file);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			finish();
		} catch (IllegalStateException e) {
			e.printStackTrace();
			finish();
		} catch (IOException e) {
			e.printStackTrace();
			finish();
		}

		try {
			mediaPlayer.prepareAsync();
		} catch (IllegalStateException e) {
			e.printStackTrace();
			finish();
		}

	}
}

git clone https://github.com/lnmcc/VideoPlayerUsingMediaPlayer.git

理想的设计过程模型

alt none

ref:《设计原本》

Boehm螺旋模型

alt none

ref:《设计原本》

AudioRecord录制音频的基本步骤

采样定理,又称香农采样定理,奈奎斯特采样定理:

​ 在进行模拟/数字信号的转换过程中,当采样频率fs.max大于信号中最高频率fmax的2倍时(fs.max >= 2fmax),采样之后的数字信号完整地保留了原始信号中的信息。 ​ 一般实际应用中保证采样频率为信号最高频率的5~10倍。要使实信号采样后能够不失真还原,采样频率必须大于信号最高频率的两倍。

	//设置声音采样率
	int frequency = 8000;

	//设置声道
	int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;

	//设置声音编码格式
	int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;

	//获取AudioRecord的最小缓冲区大小
	int minSize = AudioRecord.getMinBufferSize(frequency, 
                                                   channelConfig, 
                                                   audioEncoding);

	//录制缓冲区
	short audioBuffer = new short[BUFSZ];

	AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 
                                                  frequency, 
                                                  channelConfig, 
                                                  audioEncoding,minSize);

        //开始录制	
	audioRecord.startRecording();

	while(true) {
		audioRecord.read(audioBuffer, 0, BUFSZ);
	}

ref:http://zh.wikipedia.org/zh-cn/%E5%A5%88%E5%A5%8E%E6%96%AF%E7%89%B9%E9%87%87%E6%A0%B7%E5%AE%9A%E7%90%86

编译ffmpeg使支持PIPE输入

查看ffmpeg支持的输入协议

./configure  --list-protocols

可能的输出:

applehttp
httpproxy
rtmp
bluray    
https			
rtmpe
cache			
librtmp			
rtmps
concat			
librtmpe		
rtmpt
crypto			
librtmps		
rtmpte
ffrtmpcrypt		
librtmpt		
rtmpts
ffrtmphttp		
librtmpte		
rtp
file			
md5			
sctp
gopher			
mmsh			
tcp
hls			
mmst			
tls
http			
pipe			
udp

增加PIPE支持

./configure --enable-protocol=pipe

一个完整的编译参数

./configure  --enable-libmp3lame 
             --enable-vdpau 
             --enable-libx264 
             --enable-gpl 
             --disable-static 
             --enable-shared 
             --disable-armv5te 
             --disable-armv6 
             --disable-armv6t2 
             --disable-armvfp 
             --enable-gpl  
             --enable-protocol=pipe