diff --git a/app/build.gradle b/app/build.gradle index f021b5c..2585870 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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') } diff --git a/app/src/main/cpp/video_filter.c b/app/src/main/cpp/video_filter.c index d447d80..8f310f7 100644 --- a/app/src/main/cpp/video_filter.c +++ b/app/src/main/cpp/video_filter.c @@ -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; } \ No newline at end of file diff --git a/app/src/main/java/com/frank/ffmpeg/VideoPlayer.java b/app/src/main/java/com/frank/ffmpeg/VideoPlayer.java index b1225b1..77db8ef 100644 --- a/app/src/main/java/com/frank/ffmpeg/VideoPlayer.java +++ b/app/src/main/java/com/frank/ffmpeg/VideoPlayer.java @@ -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(); } diff --git a/app/src/main/java/com/frank/ffmpeg/activity/FilterActivity.java b/app/src/main/java/com/frank/ffmpeg/activity/FilterActivity.java index 4637046..abffa74 100644 --- a/app/src/main/java/com/frank/ffmpeg/activity/FilterActivity.java +++ b/app/src/main/java/com/frank/ffmpeg/activity/FilterActivity.java @@ -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 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; + } + } diff --git a/app/src/main/java/com/frank/ffmpeg/adapter/HorizontalAdapter.java b/app/src/main/java/com/frank/ffmpeg/adapter/HorizontalAdapter.java new file mode 100644 index 0000000..452927b --- /dev/null +++ b/app/src/main/java/com/frank/ffmpeg/adapter/HorizontalAdapter.java @@ -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 itemList = new ArrayList<>(); + private OnItemClickListener onItemClickListener; + private int lastClickPosition; + + public HorizontalAdapter(List 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); + } + } + +} diff --git a/app/src/main/java/com/frank/ffmpeg/listener/OnItemClickListener.java b/app/src/main/java/com/frank/ffmpeg/listener/OnItemClickListener.java new file mode 100644 index 0000000..6353d8f --- /dev/null +++ b/app/src/main/java/com/frank/ffmpeg/listener/OnItemClickListener.java @@ -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); +} diff --git a/app/src/main/res/layout/activity_filter.xml b/app/src/main/res/layout/activity_filter.xml index d2641e2..8bebf7e 100644 --- a/app/src/main/res/layout/activity_filter.xml +++ b/app/src/main/res/layout/activity_filter.xml @@ -8,62 +8,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - -