支持切换滤镜播放,水平滑动控件改为RecyclerView

支持切换滤镜播放,水平滑动控件改为RecyclerView
pull/107/head
frank 6 years ago
parent 8548be13ac
commit 6ea63d7cbd
  1. 3
      app/build.gradle
  2. 66
      app/src/main/cpp/video_filter.c
  3. 3
      app/src/main/java/com/frank/ffmpeg/VideoPlayer.java
  4. 127
      app/src/main/java/com/frank/ffmpeg/activity/FilterActivity.java
  5. 74
      app/src/main/java/com/frank/ffmpeg/adapter/HorizontalAdapter.java
  6. 11
      app/src/main/java/com/frank/ffmpeg/listener/OnItemClickListener.java
  7. 58
      app/src/main/res/layout/activity_filter.xml
  8. 10
      app/src/main/res/layout/item_select.xml
  9. 2
      app/src/main/res/values/strings.xml
  10. BIN
      picture/Watermark.png

@ -46,7 +46,8 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:appcompat-v7:25.4.0'
compile 'com.android.support:recyclerview-v7:25.4.0'
testCompile 'junit:junit:4.12'
compile project(':Live')
}

@ -35,14 +35,22 @@ ANativeWindow_Buffer windowBuffer;
ANativeWindow* nativeWindow;
AVFrame * pFrame;
AVFrame *pFrame;
AVFrame * pFrameRGBA;
AVFrame *pFrameRGBA;
uint8_t * buffer;
AVFrame *filter_frame;
uint8_t *buffer;
struct SwsContext *sws_ctx;
int is_playing;
int again;
int release;
/**
* colorbalance:
rs/gs/bs
@ -71,10 +79,8 @@ struct SwsContext *sws_ctx;
//const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5'";//绘制矩形
//const char *filter_descr = "drawgrid=w=iw/3:h=ih/3:t=2:c=white@0.5";//九宫格分割
//const char *filter_descr = "edgedetect=low=0.1:high=0.4";//边缘检测
//const char *filter_descr = "fftfilt=dc_Y=0:weight_Y='exp(-4 * ((Y+X)/(W+H)))'";//fft模糊
//const char *filter_descr = "lutrgb='r=0:g=0'";//去掉红色、绿色分量,只保留蓝色
//const char *filter_descr = "noise=alls=20:allf=t+u";//添加噪声
//const char *filter_descr = "pad=640:480:40:0:violet";//pad填充颜色
//const char *filter_descr = "vignette='PI/4+random(1)*PI/50':eval=frame";//闪烁装饰-->Make a flickering vignetting
//const char *filter_descr = "gblur=sigma=0.5:steps=1:planes=1:sigmaV=1";//高斯模糊
@ -167,8 +173,6 @@ int open_input(JNIEnv * env, const char* file_name, jobject surface){
LOGI("open file:%s\n", file_name);
//注册所有组件
av_register_all();
//注册滤波器
avfilter_register_all();
//分配上下文
pFormatCtx = avformat_alloc_context();
//打开视频文件
@ -246,18 +250,20 @@ JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_VideoPlayer_filter
const char * file_name = (*env)->GetStringUTFChars(env, filePath, JNI_FALSE);
const char *filter_descr = (*env)->GetStringUTFChars(env, filterDescr, JNI_FALSE);
//打开输入文件
if((ret = open_input(env, file_name, surface) < 0)){
LOGE("Couldn't allocate video frame.");
goto end;
}
//注册滤波器
avfilter_register_all();
AVFrame * filter_frame = av_frame_alloc();
if(filter_frame == NULL) {
LOGE("Couldn't allocate video frame.");
ret = -1;
goto end;
if(!is_playing){
LOGI("open_input...");
if((ret = open_input(env, file_name, surface) < 0)){
LOGE("Couldn't allocate video frame.");
goto end;
}
//注册滤波器
avfilter_register_all();
filter_frame = av_frame_alloc();
if(filter_frame == NULL) {
LOGE("Couldn't allocate filter frame.");
ret = -1;
goto end;
}
}
//初始化滤波器
@ -266,10 +272,15 @@ JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_VideoPlayer_filter
goto end;
}
is_playing = 1;
int frameFinished;
AVPacket packet;
while(av_read_frame(pFormatCtx, &packet)>=0) {
while(av_read_frame(pFormatCtx, &packet)>=0 && !release) {
//切换滤波器,退出当初播放
if(again){
goto again;
}
//判断是否为视频流
if(packet.stream_index == video_stream_index) {
//对该帧进行解码
@ -311,6 +322,7 @@ JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_VideoPlayer_filter
av_packet_unref(&packet);
}
end:
is_playing = 0;
//释放内存以及关闭文件
av_free(buffer);
av_free(pFrameRGBA);
@ -318,8 +330,22 @@ JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_VideoPlayer_filter
av_free(pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
avfilter_free(buffersrc_ctx);
avfilter_free(buffersink_ctx);
avfilter_graph_free(&filter_graph);
(*env)->ReleaseStringUTFChars(env, filePath, file_name);
(*env)->ReleaseStringUTFChars(env, filterDescr, filter_descr);
LOGE("do release...");
again:
again = 0;
LOGE("play again...");
return ret;
}
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_VideoPlayer_again(JNIEnv * env, jclass clazz) {
again = 1;
}
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_VideoPlayer_release(JNIEnv * env, jclass clazz) {
release = 1;
}

@ -14,5 +14,6 @@ public class VideoPlayer {
public native int setPlayRate(float playRate);
public native int filter(String filePath, Object surface, String filterType);
public native void again();
public native void release();
}

@ -3,14 +3,20 @@ package com.frank.ffmpeg.activity;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import com.frank.ffmpeg.R;
import com.frank.ffmpeg.VideoPlayer;
import com.frank.ffmpeg.adapter.HorizontalAdapter;
import com.frank.ffmpeg.listener.OnItemClickListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FilterActivity extends AppCompatActivity implements View.OnClickListener, SurfaceHolder.Callback{
public class FilterActivity extends AppCompatActivity implements SurfaceHolder.Callback{
//SD卡根目录
private final static String PATH = Environment.getExternalStorageDirectory().getPath();
@ -19,30 +25,46 @@ public class FilterActivity extends AppCompatActivity implements View.OnClickLis
private VideoPlayer videoPlayer;
private SurfaceHolder surfaceHolder;
//surface是否已经创建
private boolean surfaceCreated;
//是否正在播放
private boolean isPlaying;
//滤镜数组
private String[] filters = new String[]{
"lutyuv='u=128:v=128'",
"hue='h=60:s=-3'",
"lutrgb='r=0:g=0'",
"edgedetect=low=0.1:high=0.4",
"fftfilt=dc_Y=0:weight_Y='exp(-4 * ((Y+X)/(W+H)))'",
"boxblur=2:1",
"drawgrid=w=iw/3:h=ih/3:t=2:c=white@0.5",
"colorbalance=bs=0.3",
"drawbox=x=100:y=100:w=100:h=100:color=pink@0.5'",
"rotate=90",
"drawbox=x=100:y=100:w=100:h=100:color=red@0.5'",
"vignette='PI/4+random(1)*PI/50':eval=frame",
"vflip",
"noise=alls=20:allf=t+u",
"vignette='PI/4+random(1)*PI/50':eval=frame"
};
private int filterType;
private String[] txtArray = new String[]{
"素描",
"鲜明",//hue
"暖蓝",
"边缘",
"模糊",
"九宫格",
"均衡",
"矩形",
"闪烁",//左上角闪烁
"翻转"//vflip上下翻转,hflip是左右翻转
};
private HorizontalAdapter horizontalAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_filter);
initView();
registerLister();
}
private void initView(){
@ -51,63 +73,37 @@ public class FilterActivity extends AppCompatActivity implements View.OnClickLis
surfaceHolder.addCallback(this);
videoPlayer = new VideoPlayer();
findViewById(R.id.btn_sketch).setOnClickListener(this);
findViewById(R.id.btn_hue).setOnClickListener(this);
findViewById(R.id.btn_lut).setOnClickListener(this);
findViewById(R.id.btn_edge).setOnClickListener(this);
findViewById(R.id.btn_blur).setOnClickListener(this);
findViewById(R.id.btn_grid).setOnClickListener(this);
findViewById(R.id.btn_balance).setOnClickListener(this);
findViewById(R.id.btn_box).setOnClickListener(this);
findViewById(R.id.btn_rotate).setOnClickListener(this);
findViewById(R.id.btn_flip).setOnClickListener(this);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(linearLayoutManager);
List<String> itemList = new ArrayList<>();
itemList.addAll(Arrays.asList(txtArray));
horizontalAdapter = new HorizontalAdapter(itemList);
recyclerView.setAdapter(horizontalAdapter);
}
@Override
public void onClick(View v) {
if(!surfaceCreated)
return;
switch (v.getId()){
case R.id.btn_sketch://素描
filterType = 0;
break;
case R.id.btn_hue://hue
filterType = 1;
break;
case R.id.btn_lut://lut
filterType = 2;
break;
case R.id.btn_edge://边缘
filterType = 3;
break;
case R.id.btn_blur://模糊
filterType = 4;
break;
case R.id.btn_grid://九宫格
filterType = 5;
break;
case R.id.btn_balance://色彩平衡
filterType = 6;
break;
case R.id.btn_box://box绘制矩形区域
filterType = 7;
break;
case R.id.btn_rotate://旋转
filterType = 8;
break;
case R.id.btn_flip://翻转
filterType = 9;
break;
default:
filterType = 0;
break;
}
new Thread(new Runnable() {
//注册监听器
private void registerLister(){
horizontalAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void run() {
videoPlayer.filter(VIDEO_PATH, surfaceHolder.getSurface(), filters[filterType]);
public void onItemClick(int position) {
if(!surfaceCreated)
return;
final int mPosition = position;
new Thread(new Runnable() {
@Override
public void run() {
//切换播放
if(isPlaying){
videoPlayer.again();
}
isPlaying = true;
videoPlayer.filter(VIDEO_PATH, surfaceHolder.getSurface(), filters[mPosition]);
}
}).start();
}
}).start();
});
}
@Override
@ -125,4 +121,13 @@ public class FilterActivity extends AppCompatActivity implements View.OnClickLis
surfaceCreated = false;
}
@Override
protected void onDestroy() {
super.onDestroy();
isPlaying = false;
videoPlayer.release();
videoPlayer = null;
horizontalAdapter = null;
}
}

@ -0,0 +1,74 @@
package com.frank.ffmpeg.adapter;
import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.frank.ffmpeg.R;
import com.frank.ffmpeg.listener.OnItemClickListener;
import java.util.ArrayList;
import java.util.List;
/**
* RecyclerView适配器
* Created by frank on 2018/6/6.
*/
public class HorizontalAdapter extends RecyclerView.Adapter{
private List<String> itemList = new ArrayList<>();
private OnItemClickListener onItemClickListener;
private int lastClickPosition;
public HorizontalAdapter(List<String> itemList){
this.itemList = itemList;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new OkViewHolder(LayoutInflater.from(parent.getContext()).
inflate(R.layout.item_select, parent, false));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final OkViewHolder okViewHolder = (OkViewHolder)holder;
okViewHolder.btn_select.setText(itemList.get(position));
okViewHolder.btn_select.setTextColor(Color.DKGRAY);
if(onItemClickListener != null){
okViewHolder.btn_select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
notifyItemChanged(lastClickPosition);
Log.i("onBindViewHolder", "lastClickPosition=" + lastClickPosition);
//设置当前选中颜色
okViewHolder.btn_select.setTextColor(Color.BLUE);
onItemClickListener.onItemClick(okViewHolder.getAdapterPosition());
lastClickPosition = okViewHolder.getAdapterPosition();
}
});
}
}
@Override
public int getItemCount() {
return itemList != null ? itemList.size() : 0;
}
private class OkViewHolder extends RecyclerView.ViewHolder{
Button btn_select;
OkViewHolder(View itemView) {
super(itemView);
btn_select = (Button)itemView.findViewById(R.id.btn_select);
}
}
}

@ -0,0 +1,11 @@
package com.frank.ffmpeg.listener;
/**
* RecyclerView item点击监听器
* Created by frank on 2018/6/6.
*/
public interface OnItemClickListener {
void onItemClick(int position);
}

@ -8,62 +8,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:scrollbarAlwaysDrawHorizontalTrack="true"
android:layout_alignParentBottom="true">
<Button
android:id="@+id/btn_sketch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_sketch"/>
<Button
android:id="@+id/btn_hue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_hue"/>
<Button
android:id="@+id/btn_lut"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_lut"/>
<Button
android:id="@+id/btn_edge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_edge"/>
<Button
android:id="@+id/btn_blur"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_blur"/>
<Button
android:id="@+id/btn_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_grid"/>
<Button
android:id="@+id/btn_balance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_balance"/>
<Button
android:id="@+id/btn_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_box"/>
<Button
android:id="@+id/btn_rotate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_rotate"/>
<Button
android:id="@+id/btn_flip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_flip"/>
</LinearLayout>
android:layout_alignParentBottom="true"/>
</RelativeLayout>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content">
<Button
android:id="@+id/btn_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

@ -50,7 +50,7 @@
<string name="video_rotate">旋转</string>
<string name="video_flip">翻转</string>
<string name="video_box">矩形</string>
<string name="video_noise">噪声</string>
<string name="video_flash">闪烁</string>
<string name="video_balance">色彩平衡</string>
</resources>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

Loading…
Cancel
Save