不多说,上代码
这里是关于对视频抽取的直接操作
public class ExtractorMuxer {
public static void divideMedia (@Nullable String srcPath, @Nullable String dstPath) throws IOException {
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(srcPath);
MediaMuxer muxer = new MediaMuxer(dstPath,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
MediaFormat format =null;
int audioIndex =-1;
int videoIndex =-1;
for(int i=0;i<extractor.getTrackCount();i++){
format = extractor.getTrackFormat(i);
if(format.getString(MediaFormat.KEY_MIME).startsWith("audio")){
audioIndex = i;
continue;
}
if (format.getString(MediaFormat.KEY_MIME).startsWith("video")){
videoIndex = i;
}
}
int audioTrack = -1;
int videoTrack = -1;
audioTrack = muxer.addTrack(extractor.getTrackFormat(audioIndex));
videoTrack = muxer.addTrack(extractor.getTrackFormat(videoIndex));
muxer.start();
extractor.selectTrack(audioIndex);
writeIntoBuf(extractor,muxer,audioTrack);
extractor.selectTrack(videoIndex);
writeIntoBuf(extractor,muxer,videoTrack);
muxer.stop();
muxer.release();
extractor.release();
}
private static void writeIntoBuf(@Nullable MediaExtractor extractor,@Nullable MediaMuxer mediaMuxer,int track){
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
info.presentationTimeUs =0;
ByteBuffer byteBuffer = ByteBuffer.allocate(1024*1024);
if (extractor!=null&&mediaMuxer!=null) {
while (true) {
int size = extractor.readSampleData(byteBuffer, 0);
if (size <= 0) {
break;
} else {
//当视频出现拖拉后花屏现象时,可能是info.flags设置成了MediaCodec.Buffer.Flag.Key.Frame了,MediaMuxer在写入时自动将其他帧视为I帧导致信息错误从而缺失P帧出现花屏。
info.flags = extractor.getSampleFlags();
info.offset = 0;
info.presentationTimeUs = extractor.getSampleTime();
info.size = size;
mediaMuxer.writeSampleData(track, byteBuffer, info);
extractor.advance();
}
}
}
}
}
这里是简单的利用systemvideoview完成视频的播放,由于需要拷贝到本地存储,所以略微复杂了一些
*/
public class MainActivity extends Activity {
String filePath=null;
String path=null;
private VideoView systemVideoView;
private MediaController mediaController;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
filePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/map1.mp4";
path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/map4.mp4";
systemVideoView = findViewById(R.id.videoView);
mediaController = new MediaController(this);
mediaController.show();
systemVideoView.setMediaController(mediaController);
if (verifyPermissino(this)) {
Log.v("tag", "写入sdcard");
saveToSDCard(filePath, getResources().openRawResource(R.raw.shape_of_my_heart));
}
try {
ExtractorMuxer.divideMedia(filePath,path);
} catch (IOException e) {
e.printStackTrace();
}
systemVideoView.setVideoURI(Uri.fromFile(new File(path)));
}
private void saveToSDCard(String filename, InputStream is) {
File file = new File(filename);
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] bytes = new byte[1024*1024];
int size = -1;
while (true) {
try{
if(((size=is.read(bytes))>0)){
os.write(bytes,0,size);
}else{
break;
}
}catch (IOException e){
e.printStackTrace();
}
}
}
private boolean verifyPermissino(Activity activity){
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(permission!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,PERMISSIONS_STORAGE,1);
}
return permission==PackageManager.PERMISSION_GRANTED;
}
}
总结:
1.拷贝到本地存储起来并完成对本地视频文件的读写,需要完成权限的申请。manifest文件里还要申请权限就不列出来了。
2.使用systemvideoview或mediaplayer,直接利用文件的绝对路径也无法完成最后的播放,因为会抛出no contentProvider 的filenotfoundexception异常,所以笔者就采用了uri播放,但是常规的uri.parse是行不通的,还是会抛同样的异常,这里用了uri.fromFile就可以完美的规避这个异常。
3.如果在readSampleData中出了illegalArgumentException,记住,如果确认自己的参数类型没有丝毫问题,那么就可能出在bytebuffer的大小上,我第一次使用Input_max_size的常量作为buffer的大小,但是因为新版本问题,用不了。第二次随便申请了100kb的大小,之前只分离音频的时候还行,后面用到音视频的分离的时候就出错了。于是不得不直接申请两个1mb大小的buf去完成音视频数据的读写。