package lzf.video.edit; import android.Manifest; import android.annotation.TargetApi; import android.content.Intent; import android.content.pm.PackageManager; import android.media.MediaCodec; import android.media.MediaExtractor; import android.media.MediaFormat; import android.media.MediaMuxer; import android.os.AsyncTask; import android.os.Build; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.VideoView; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import lzf.video.edit.decoder.AudioDecoder; import lzf.video.edit.decoder.AudioEncoder; import lzf.video.edit.decoder.CustomAudioDecoder; import lzf.video.edit.decoder.MultiAudioMixer; import lzf.video.edit.entry.AudioEntry; import lzf.video.edit.utils.MD5Util; public class ChangeBackgroundAudioActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "ChangeBgAudioActivity"; private final static int REQUEST_CODE_ADD_MUSIC = 0x1; private final static int PERMISSION_DECODER_MUSIC = 8005; private String videoPath = null; private VideoView videoView; private ImageView play_video; private ImageView add_music; private ImageView play_music; private TextView complex; private LinearLayout music; private TextView music_name; private TextView music_time; private EditText start_time; private EditText end_time; private TextView change_music; private AudioEntry audioEntry; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_change_audio); initView(); initListener(); getVideoPath(); play_video.setVisibility(View.GONE); music.setVisibility(View.GONE); if (videoPath != null) { videoView.setVideoPath(videoPath); videoView.start(); } } private void initListener() { play_video.setOnClickListener(this); play_music.setOnClickListener(this); add_music.setOnClickListener(this); complex.setOnClickListener(this); change_music.setOnClickListener(this); } private void initView() { videoView = (VideoView) findViewById(R.id.videoView); play_video = (ImageView) findViewById(R.id.play); add_music = (ImageView) findViewById(R.id.add_music); play_music = (ImageView) findViewById(R.id.play_music); complex = (TextView) findViewById(R.id.complex); music = (LinearLayout) findViewById(R.id.music); music_name = (TextView) findViewById(R.id.music_name); music_time = (TextView) findViewById(R.id.music_time); start_time = (EditText) findViewById(R.id.start_time); end_time = (EditText) findViewById(R.id.end_time); change_music = (TextView) findViewById(R.id.change_music); } public void getVideoPath() { if (getIntent() != null) { videoPath = getIntent().getStringExtra("video_path"); Log.e(TAG, "视频路径" + videoPath); } else { Log.e(TAG, "获取视频路径失败"); } } @Override public void onClick(View v) { switch (v.getId()) { //添加音乐 case R.id.add_music: Intent intent = new Intent(this, SelectLocalAudioListActivity.class); startActivityForResult(intent, REQUEST_CODE_ADD_MUSIC); break; //播放视频 case R.id.play: if (videoPath != null) { videoView.setVideoPath(videoPath); videoView.start(); } else { Log.e(TAG, "获取视频路径为空"); } break; //播放音乐 case R.id.play_music: break; //合成音乐和视频 case R.id.complex: //音视频合并 耗时比较久 MP3先转成acc在与video合并 videoComplexAudio(); break; //剪切音频 获得的是MP3 case R.id.change_music: checkAudioTime(); break; } } private void videoComplexAudio() { Log.e("lzf_audio_acc",audioEntry.fileUrl); mp3ToAcc(); Log.e("lzf_audio_acc",audioEntry.fileUrl); muxerVideo(videoPath,audioEntry.fileUrl); } private void mp3ToAcc(){ AudioEncoder accEncoder = AudioEncoder.createAccEncoder(audioEntry.fileUrl); String finalMixPath = Environment.getExternalStorageDirectory().getPath() + "/videoEdit_MixAudioTest.acc"; accEncoder.encodeToFile(finalMixPath); audioEntry.fileUrl=finalMixPath; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private void muxerVideo(String videoPath, String accPath) { try { //获取视频 MediaExtractor videoExtractor = new MediaExtractor(); videoExtractor.setDataSource(videoPath); MediaFormat videoFormat = null; int videoTrackIndex = -1; int videoTrackCount = videoExtractor.getTrackCount(); for (int i = 0; i < videoTrackCount; i++) { videoFormat = videoExtractor.getTrackFormat(i); String type = videoFormat.getString(MediaFormat.KEY_MIME); if (type.startsWith("video/")) { videoTrackIndex = i; break; } } //获取音频 MediaExtractor audioExtractor = new MediaExtractor(); audioExtractor.setDataSource(accPath); MediaFormat audioFormat = null; int audioTrackIndex = -1; int audioTrackCount = audioExtractor.getTrackCount(); for (int i = 0; i < audioTrackCount; i++) { audioFormat = audioExtractor.getTrackFormat(i); String type = audioFormat.getString(MediaFormat.KEY_MIME); if (type.startsWith("audio/")) { audioTrackIndex = i; break; } } //设置数据信道 videoExtractor.selectTrack(videoTrackIndex); audioExtractor.selectTrack(audioTrackIndex); MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo(); MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo(); //输出路径 String videoPath_muxer=Environment.getExternalStorageDirectory() +"/"+(System.currentTimeMillis()+"output.mp4"); MediaMuxer mediaMuxer = new MediaMuxer(videoPath_muxer, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); int writeVideoTrackIndex = mediaMuxer.addTrack(videoFormat); int writeAudioTrackIndex = mediaMuxer.addTrack(audioFormat); Log.e("lzf_index", writeVideoTrackIndex + " " + writeAudioTrackIndex); mediaMuxer.start(); ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024); long sampleTime = 0; { videoExtractor.readSampleData(byteBuffer, 0); if (videoExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) { videoExtractor.advance(); } videoExtractor.readSampleData(byteBuffer, 0); long secondTime = videoExtractor.getSampleTime(); videoExtractor.advance(); long thirdTime = videoExtractor.getSampleTime(); sampleTime = Math.abs(thirdTime - secondTime); } videoExtractor.unselectTrack(videoTrackIndex); videoExtractor.selectTrack(videoTrackIndex); //写入视频 while (true) { int readVideoSampleSize = videoExtractor.readSampleData(byteBuffer, 0); if (readVideoSampleSize < 0) { break; } videoBufferInfo.size = readVideoSampleSize; videoBufferInfo.presentationTimeUs += sampleTime; videoBufferInfo.offset = 0; videoBufferInfo.flags = videoExtractor.getSampleFlags(); mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videoBufferInfo); videoExtractor.advance(); } //写入音频 while (true) { int readAudioSampleSize = audioExtractor.readSampleData(byteBuffer, 0); if (readAudioSampleSize < 0) { break; } audioBufferInfo.size = readAudioSampleSize; audioBufferInfo.presentationTimeUs += sampleTime; audioBufferInfo.offset = 0; audioBufferInfo.flags = videoExtractor.getSampleFlags(); mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBuffer, audioBufferInfo); audioExtractor.advance(); } //释放 mediaMuxer.stop(); mediaMuxer.release(); videoExtractor.release(); audioExtractor.release(); play_video.setVisibility(View.VISIBLE); videoView.setVideoPath(videoPath_muxer); videoView.start(); this.videoPath=videoPath_muxer; } catch (IOException e) { e.printStackTrace(); } } /* * 检验输入的合法性 * */ private void checkAudioTime() { if (start_time.getText() != null) { if (!(Integer.parseInt(start_time.getText().toString()) < 0 || Integer.parseInt(start_time.getText().toString()) > audioEntry.duration/1000)) { if (end_time.getText() != null) { if (!(Integer.parseInt(end_time.getText().toString()) <= Integer.parseInt(start_time.getText().toString()) || Integer.parseInt(end_time.getText().toString()) >= audioEntry.duration/1000)) { //检验权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { if (audioEntry != null) { decodeMusic(audioEntry.fileUrl, Integer.parseInt(start_time.getText().toString()), Integer.parseInt(end_time.getText().toString())); } } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_DECODER_MUSIC); } } else { Toast.makeText(this, "错误的结束时间", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "请输入结束时间", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "错误的开始时间", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "请输入开始时间", Toast.LENGTH_SHORT).show(); } } /* * 分割音频 * */ private void decodeMusic(String audioPath, int fromTime, int endTime) { String decode_file = null; try { decode_file = CustomAudioDecoder.fenLiData(audioPath); } catch (IOException e) { e.printStackTrace(); } List<Integer> list = null; if (decode_file != null) { list = CustomAudioDecoder.initMP3Frame(decode_file); } if (list != null) { try { final String path = CustomAudioDecoder.CutingMp3(decode_file, "分离", list, fromTime, endTime); Log.e("lzf_music", "分离路径" + path); AudioEntry mAudioEntry = new AudioEntry(); mAudioEntry.fileName="分离"; mAudioEntry.fileUrl=path; mAudioEntry.duration=endTime-fromTime; mAudioEntry.album=audioEntry.album; mAudioEntry.mime=audioEntry.mime; music_name.setText(mAudioEntry.fileName); music_time.setText("音频时长"+mAudioEntry.duration+"秒"); change_music.setText("转码中 请稍后"); new DecodeAudioTask(mAudioEntry).execute(); } catch (IOException e) { e.printStackTrace(); } final File file = new File(decode_file); file.delete(); } else { Log.e("lzf_music", "分离失败"); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_ADD_MUSIC && resultCode == RESULT_OK) { //设置音频信息 显示音乐 audioEntry = (AudioEntry) data.getSerializableExtra("audio"); music.setVisibility(View.VISIBLE); music_name.setText(audioEntry.fileName); try { music_time.setText(audioEntry.duration/1000+"秒"); } catch (Exception e) { e.printStackTrace(); } Log.e("lzf——audio mime", audioEntry.mime); Log.e("lzf——audio", audioEntry.toString()); } else { Toast.makeText(this, "返回失败", Toast.LENGTH_SHORT).show(); } } class DecodeAudioTask extends AsyncTask<Void, Double, Boolean> { AudioEntry decAudio; DecodeAudioTask(AudioEntry decAudio) { this.decAudio = decAudio; } @Override protected Boolean doInBackground(Void... params) { String decodeFilePath = Environment.getExternalStorageDirectory().getPath() + "/" + MD5Util.getMD5Str(decAudio.fileUrl); File decodeFile = new File(decodeFilePath); if (decodeFile.exists()) { publishProgress(1.0); decAudio.fileUrl = decodeFilePath; return true; } if (decAudio.mime.contains("x-ms-wma")) { FileInputStream fisWavFile = null; FileOutputStream fosRawAudioFile = null; try { File srcAudioFile = new File(decAudio.fileUrl); long audioFileSize = srcAudioFile.length(); fisWavFile = new FileInputStream(srcAudioFile); fosRawAudioFile = new FileOutputStream(decodeFile); fisWavFile.read(new byte[44]); byte[] rawBuf = new byte[1024]; int readCount; double totalReadCount = 44; while ((readCount = fisWavFile.read(rawBuf)) != -1) { fosRawAudioFile.write(rawBuf, 0, readCount); totalReadCount += readCount; publishProgress(totalReadCount / audioFileSize); } publishProgress(1.0); return true; } catch (IOException e) { e.printStackTrace(); } finally { try { if (fisWavFile != null) fisWavFile.close(); if (fosRawAudioFile != null) fosRawAudioFile.close(); } catch (IOException e) { e.printStackTrace(); } } return false; } else { AudioDecoder audioDec = AudioDecoder.createDefualtDecoder(decAudio.fileUrl); try { decAudio.fileUrl = decodeFilePath; audioDec.setOnAudioDecoderListener(new AudioDecoder.OnAudioDecoderListener() { @Override public void onDecode(byte[] decodedBytes, double progress) throws IOException { publishProgress(progress); } }); audioDec.decodeToFile(decodeFilePath); return true; } catch (IOException e) { e.printStackTrace(); return false; } } } @Override protected void onProgressUpdate(Double... values) { super.onProgressUpdate(values); Log.e("lzf_进度", values + " "); } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); change_music.setText("转码完成"); Log.e("lzf_进度", "转码完成"); if (result) { addMusicTrack(decAudio); } } private void addMusicTrack(final AudioEntry decAudio) { audioEntry=decAudio; } } }