增加音视频合成与分离

使用FFmpeg实现音视频合成、抽取音频、抽取视频
pull/107/head
xufuji456 7 years ago
parent 8f760dbea4
commit b2ef61cc53
  1. 6
      .idea/vcs.xml
  2. 5
      README.md
  3. 8
      app/src/main/AndroidManifest.xml
  4. 49
      app/src/main/java/com/frank/ffmpeg/activity/MainActivity.java
  5. 168
      app/src/main/java/com/frank/ffmpeg/activity/MediaHandleActivity.java
  6. 20
      app/src/main/java/com/frank/ffmpeg/activity/VideoHandleActivity.java
  7. 42
      app/src/main/java/com/frank/ffmpeg/util/FFmpegUtil.java
  8. 4
      app/src/main/res/drawable/btn.xml
  9. 0
      app/src/main/res/drawable/btn_circle.xml
  10. 0
      app/src/main/res/drawable/btn_point.xml
  11. 0
      app/src/main/res/drawable/white_background.xml
  12. 283
      app/src/main/res/layout/activity_main.xml
  13. 34
      app/src/main/res/layout/activity_media_handle.xml
  14. 26
      app/src/main/res/layout/activity_splash.xml
  15. 8
      app/src/main/res/layout/activity_video_handle.xml
  16. 19
      app/src/main/res/values/strings.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -6,8 +6,11 @@ android端基于FFmpeg库在中的使用。<br>
- #### 音频剪切、拼接 - #### 音频剪切、拼接
- #### 音频混音 - #### 音频混音
- #### 音频转码 - #### 音频转码
- #### 音视频合成
- #### 音频抽取
- #### 视频抽取
*** ***
后续会加上音视频编解码、合成与分离,视频水印、截图、转Gif动图、剪切、转码。 后续会加上音视频编解码,视频水印、截图、转Gif动图、剪切、转码。
<br><br> <br><br>

@ -21,13 +21,19 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity android:name=".activity.AudioHandleActivity"> <activity android:name=".activity.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activity.AudioHandleActivity"/>
<activity android:name=".activity.MediaHandleActivity"/>
<activity android:name=".activity.VideoHandleActivity"/>
</application> </application>
</manifest> </manifest>

@ -0,0 +1,49 @@
package com.frank.ffmpeg.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import com.frank.ffmpeg.R;
/**
* 使用ffmpeg进行音视频处理入口
* Created by frank on 2018/1/23.
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
findViewById(R.id.btn_audio).setOnClickListener(this);
findViewById(R.id.btn_media).setOnClickListener(this);
findViewById(R.id.btn_video).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_audio://音频处理
startActivity(new Intent(MainActivity.this, AudioHandleActivity.class));
break;
case R.id.btn_media://音视频处理
startActivity(new Intent(MainActivity.this, MediaHandleActivity.class));
break;
case R.id.btn_video://视频处理
Toast.makeText(this, "暂时还没完善该功能...", Toast.LENGTH_LONG).show();
// startActivity(new Intent(MainActivity.this, VideoHandleActivity.class));
break;
default:
break;
}
}
}

@ -0,0 +1,168 @@
package com.frank.ffmpeg.activity;
import android.annotation.SuppressLint;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.frank.ffmpeg.FFmpegCmd;
import com.frank.ffmpeg.R;
import com.frank.ffmpeg.util.FFmpegUtil;
import java.io.File;
/**
* 使用ffmpeg进行音视频合成与分离
* Created by frank on 2018/1/23.
*/
public class MediaHandleActivity extends AppCompatActivity implements View.OnClickListener{
private final static String TAG = MediaHandleActivity.class.getSimpleName();
private static final String PATH = Environment.getExternalStorageDirectory().getPath();
private String srcFile = PATH + File.separator + "hello.mp4";
private String videoFile = PATH + File.separator + "flash-tree.mp4";//flash-tree.mp4
String temp = PATH + File.separator + "temp.mp4";
private boolean isMux;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 100){
String audioFile = PATH + File.separator + "tiger.mp3";//tiger.mp3
String muxFile = PATH + File.separator + "media-mux.mp4";
try {
//使用MediaPlayer获取视频时长
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(videoFile);
mediaPlayer.prepare();
//单位为ms
int videoDuration = mediaPlayer.getDuration()/1000;
Log.i(TAG, "videoDuration=" + videoDuration);
mediaPlayer.release();
//使用MediaMetadataRetriever获取音频时长
MediaMetadataRetriever mediaRetriever = new MediaMetadataRetriever();
mediaRetriever.setDataSource(audioFile);
//单位为ms
String duration = mediaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int audioDuration = (int)(Long.parseLong(duration)/1000);
Log.i(TAG, "audioDuration=" + audioDuration);
mediaRetriever.release();
//如果视频时长比音频长,采用音频时长,否则用视频时长
int mDuration = Math.min(audioDuration, videoDuration);
//使用纯视频与音频进行合成
String[] commandLine = FFmpegUtil.mediaMux(temp, audioFile, mDuration, muxFile);
executeFFmpegCmd(commandLine);
isMux = false;
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media_handle);
initView();
}
private void initView() {
findViewById(R.id.btn_mux).setOnClickListener(this);
findViewById(R.id.btn_extract_audio).setOnClickListener(this);
findViewById(R.id.btn_extract_video).setOnClickListener(this);
}
@Override
public void onClick(View v) {
int handleType;
switch (v.getId()){
case R.id.btn_mux:
handleType = 0;
break;
case R.id.btn_extract_audio:
handleType = 1;
break;
case R.id.btn_extract_video:
handleType = 2;
break;
default:
handleType = 0;
break;
}
doHandleMedia(handleType);
}
/**
* 调用ffmpeg处理音视频
* @param handleType handleType
*/
private void doHandleMedia(int handleType){
String[] commandLine = null;
switch (handleType){
case 0://音视频合成
try {
//视频文件有音频,先把纯视频文件抽取出来
commandLine = FFmpegUtil.extractVideo(videoFile, temp);
isMux = true;
} catch (Exception e) {
e.printStackTrace();
}
break;
case 1://提取音频
String extractAudio = PATH + File.separator + "extractAudio.aac";
commandLine = FFmpegUtil.extractAudio(srcFile, extractAudio);
break;
case 2://提取视频
String extractVideo = PATH + File.separator + "extractVideo.mp4";
commandLine = FFmpegUtil.extractVideo(srcFile, extractVideo);
break;
default:
break;
}
executeFFmpegCmd(commandLine);
}
/**
* 执行ffmpeg命令行
* @param commandLine commandLine
*/
private void executeFFmpegCmd(final String[] commandLine){
if(commandLine == null){
return;
}
FFmpegCmd.execute(commandLine, new FFmpegCmd.OnHandleListener() {
@Override
public void onBegin() {
Log.i(TAG, "handle media onBegin...");
}
@Override
public void onEnd(int result) {
Log.i(TAG, "handle media onEnd...");
if(isMux){
mHandler.obtainMessage(100).sendToTarget();
}else {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MediaHandleActivity.this, "handle media finish...", Toast.LENGTH_SHORT).show();
}
});
}
}
});
}
}

@ -0,0 +1,20 @@
package com.frank.ffmpeg.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.frank.ffmpeg.R;
/**
* 使用ffmpeg进行视频处理
* Created by frank on 2018/1/23.
*/
public class VideoHandleActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_handle);
}
}

@ -64,4 +64,46 @@ public class FFmpegUtil {
//混音公式:value = sample1 + sample2 - (sample1 * sample2 / (pow(2, 16-1) - 1)) //混音公式:value = sample1 + sample2 - (sample1 * sample2 / (pow(2, 16-1) - 1))
/**
* 使用ffmpeg命令行进行音视频合成
* @param videoFile 视频文件
* @param audioFile 音频文件
* @param duration 视频时长
* @param muxFile 目标文件
* @return 合成后的文件
*/
@SuppressLint("DefaultLocale")
public static String[] mediaMux(String videoFile, String audioFile, int duration, String muxFile){
//-t:时长 如果忽略音视频时长,则把"-t %d"去掉
String mixAudioCmd = "ffmpeg -i %s -i %s -t %d -acodec copy %s";
mixAudioCmd = String.format(mixAudioCmd, videoFile, audioFile, duration, muxFile);
return mixAudioCmd.split(" ");//以空格分割为字符串数组
}
/**
* 使用ffmpeg命令行进行抽取音频
* @param srcFile 原文件
* @param targetFile 目标文件
* @return 抽取后的音频文件
*/
public static String[] extractAudio(String srcFile, String targetFile){
//-vn:video not
String mixAudioCmd = "ffmpeg -i %s -acodec copy -vn %s";
mixAudioCmd = String.format(mixAudioCmd, srcFile, targetFile);
return mixAudioCmd.split(" ");//以空格分割为字符串数组
}
/**
* 使用ffmpeg命令行进行抽取视频
* @param srcFile 原文件
* @param targetFile 目标文件
* @return 抽取后的视频文件
*/
public static String[] extractVideo(String srcFile, String targetFile){
//-an audio not
String mixAudioCmd = "ffmpeg -i %s -vcodec copy -an %s";
mixAudioCmd = String.format(mixAudioCmd, srcFile, targetFile);
return mixAudioCmd.split(" ");//以空格分割为字符串数组
}
} }

@ -4,6 +4,6 @@
android:shape="rectangle" > android:shape="rectangle" >
<corners android:radius="10dp" /><!-- 圆角半径 --> <corners android:radius="10dp" /><!-- 圆角半径 -->
<gradient <gradient
android:endColor="@color/orange" android:endColor="@color/colorPrimary"
android:startColor="@color/orange" /><!-- 矩形的内部颜色,这里设置成白色,即空心 --> android:startColor="@color/colorAccent" /><!-- 矩形的内部颜色,这里设置成白色,即空心 -->
</shape> </shape>

@ -1,265 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
tools:context="com.frank.MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:id="@+id/playerPlan">
<!--渲染层-->
<SurfaceView
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/surfaceView"/>
<VideoView
android:id="@+id/videoPreview"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/imgPreview"
android:scaleType="fitXY"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.ant.liao.GifView android:id="@+id/gifView"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:enabled="false" />
</RelativeLayout>
<LinearLayout
android:padding="5dp"
android:id="@+id/timePlan"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="00:00"
android:id="@+id/currentTime_t"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<SeekBar
android:id="@+id/seekBar"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:maxHeight="10dp"/>
<TextView
android:text="00:00"
android:id="@+id/totalTime_t"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:background="@color/trasnsWhite"
android:padding="4dp"
android:id="@+id/controlPlan"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:textSize="10sp"
android:layout_margin="@dimen/item_margin_small"
android:background="@drawable/btn"
android:layout_width="70dp"
android:layout_height="30dp"
android:id="@+id/switchCameraBtn"
android:text="切换镜头"/>
<Button
android:text="选择图片"
android:layout_margin="@dimen/item_margin_small"
android:background="@drawable/btn"
android:id="@+id/imgBtn"
android:textSize="10sp"
android:layout_width="70dp"
android:layout_height="30dp" />
<Button
android:text="选择视频"
android:layout_margin="@dimen/item_margin_small"
android:background="@drawable/btn"
android:id="@+id/movBtn"
android:textSize="10sp"
android:layout_width="70dp"
android:layout_height="30dp" />
<Button
android:text="选择音乐"
android:layout_margin="@dimen/item_margin_small"
android:background="@drawable/btn"
android:id="@+id/musicBtn"
android:textSize="10sp"
android:layout_width="70dp"
android:layout_height="30dp" />
<Button
android:layout_margin="@dimen/item_margin_small"
android:background="@drawable/btn"
android:text="修改标题"
android:id="@+id/titleBtn"
android:textSize="10sp"
android:layout_width="70dp"
android:layout_height="30dp" />
<Button
android:layout_margin="@dimen/item_margin_small"
android:background="@drawable/btn"
android:text="合成视频"
android:id="@+id/makeBtn"
android:textSize="10sp"
android:layout_width="70dp"
android:layout_height="30dp" />
<Button
android:background="@drawable/btn"
android:layout_margin="@dimen/item_margin_small"
android:text="testBtn"
android:id="@+id/testBtn"
android:textSize="10sp"
android:layout_width="70dp"
android:layout_height="30dp"/>
</LinearLayout>
<Button <Button
android:layout_margin="4dp" android:id="@+id/btn_audio"
android:background="@drawable/btn" android:layout_width="wrap_content"
android:text="再来一发" android:layout_height="wrap_content"
android:id="@+id/newBtn" android:layout_marginTop="120dp"
android:textSize="10sp" android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true" android:text="@string/audio_handle"/>
android:layout_width="70dp"
android:layout_height="30dp" /> <Button
<FrameLayout android:id="@+id/btn_media"
android:background="@drawable/btncircle" android:layout_width="wrap_content"
android:id="@+id/recodePlan"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="10dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true">
<Button
android:layout_gravity="center"
android:background="@drawable/btnpoint"
android:id="@+id/cameraBtn"
android:layout_width="40dp"
android:layout_height="40dp" />
<Button
android:layout_gravity="center"
android:background="@color/redBtn"
android:id="@+id/stopCameraBtn"
android:layout_width="30dp"
android:layout_height="30dp" />
</FrameLayout>
<LinearLayout
android:orientation="vertical"
android:id="@+id/titlePlan"
android:padding="4dp"
android:background="@drawable/whitebg"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="40dp" android:text="@string/media_handle"
android:layout_marginRight="40dp" android:layout_marginTop="10dp"
android:layout_centerVertical="true" android:layout_centerHorizontal="true"
android:layout_centerHorizontal="true"> android:layout_below="@+id/btn_audio"/>
<TextView
android:text="输入视频信息,显示三秒片头字幕" <Button
android:layout_gravity="center" android:id="@+id/btn_video"
android:layout_margin="5dp"
android:textColor="@android:color/darker_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:orientation="horizontal"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="标题:"
android:layout_marginRight="5dp"
android:textColor="@android:color/darker_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:text="测试视频abcdefg"
android:maxLines="1"
android:padding="5dp"
android:background="@color/gray"
android:id="@+id/title_t"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="作者:"
android:layout_marginRight="5dp"
android:textColor="@android:color/darker_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:maxLines="1"
android:text="qzd"
android:padding="5dp"
android:background="@color/gray"
android:id="@+id/author_t"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="描述:"
android:layout_marginRight="5dp"
android:textColor="@android:color/darker_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:maxLines="1"
android:text="没什么好说的"
android:padding="5dp"
android:background="@color/gray"
android:id="@+id/description_t"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:text="就噶"
android:background="@drawable/btn"
android:layout_margin="5dp"
android:id="@+id/enter_Btn"
android:layout_width="match_parent"
android:layout_height="30dp" />
<TextView
android:text="做好事不留名,跳过!"
android:padding="5dp"
android:id="@+id/skip_t"
android:textColor="@android:color/darker_gray"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<!--缓冲图标-->
<LinearLayout
android:id="@+id/bufferIcon"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
<ProgressBar android:text="@string/video_handle"
android:layout_width="wrap_content" android:layout_marginTop="10dp"
android:layout_height="wrap_content"/> android:layout_centerHorizontal="true"
<TextView android:layout_below="@+id/btn_media"/>
android:id="@+id/infoText"
android:textColor="@android:color/white"
android:text="合成中,请等待。几分钟就压完了(吧)。"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout> </RelativeLayout>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.frank.ffmpeg.activity.MediaHandleActivity">
<Button
android:id="@+id/btn_mux"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="120dp"
android:text="@string/media_mux"/>
<Button
android:id="@+id/btn_extract_audio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/media_extra_audio"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_mux"/>
<Button
android:id="@+id/btn_extract_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/media_extract_video"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_extract_audio"/>
</RelativeLayout>

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@android:color/white"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.frank.splashActivity">
<TextView
android:gravity="center"
android:text="MAKE\nSOMETHING\nAND\nDON'T\nPANIC"
android:id="@+id/title_t"
android:textSize="40sp"
android:textColor="@color/orange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
<TextView
android:text="VIDEO MAKER DEMO v0.11"
android:textSize="16sp"
android:textColor="@color/gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.frank.ffmpeg.activity.VideoHandleActivity">
</RelativeLayout>

@ -4,4 +4,23 @@
<string name="audio_cut">音频剪切</string> <string name="audio_cut">音频剪切</string>
<string name="audio_concat">音频合并</string> <string name="audio_concat">音频合并</string>
<string name="audio_mix">音频混合</string> <string name="audio_mix">音频混合</string>
<string name="audio_handle">音频处理</string>
<string name="media_handle">音视频处理</string>
<string name="video_handle">视频处理</string>
<string name="media_mux">音视频合成</string>
<string name="media_extra_audio">提取音频</string>
<string name="media_extract_video">提取视频</string>
<string name="video_cut">视频剪切</string>
<string name="video_concat">视频拼接</string>
<string name="video_text_mark">视频文字水印</string>
<string name="video_photo_mark">视频图片水印</string>
<string name="video_to_gif">视频转Gif</string>
<string name="video_transform">视频转码</string>
<string name="video_screen_shot">视频截图</string>
<string name="video_extract_frame">视频抽帧</string>
<string name="video_part_zoom">视频局部放大</string>
</resources> </resources>

Loading…
Cancel
Save