FFmpeg音视频解码播放

pull/107/head
frank 7 years ago
parent 64e00b1f4f
commit c1fb401774
  1. 4
      app/CMakeLists.txt
  2. 16
      app/src/main/AndroidManifest.xml
  3. 63
      app/src/main/cpp/AVpacket_queue.c
  4. 25
      app/src/main/cpp/AVpacket_queue.h
  5. 505
      app/src/main/cpp/media_player.c
  6. 45
      app/src/main/java/com/frank/ffmpeg/MediaPlayer.java
  7. 17
      app/src/main/java/com/frank/ffmpeg/activity/MainActivity.java
  8. 81
      app/src/main/java/com/frank/ffmpeg/activity/MediaPlayerActivity.java
  9. 2
      app/src/main/java/com/frank/ffmpeg/activity/VideoPlayerActivity.java
  10. 2
      app/src/main/res/layout/activity_audio_handle.xml
  11. 26
      app/src/main/res/layout/activity_main.xml
  12. 33
      app/src/main/res/layout/activity_media_player.xml
  13. 4
      app/src/main/res/values/strings.xml

@ -26,7 +26,9 @@ add_library( # Sets the name of the library.
src/main/cpp/audio_player.c
src/main/cpp/openSL_audio_player.c
src/main/cpp/video_player.c
src/main/cpp/ffmpeg_pusher.cpp)
src/main/cpp/ffmpeg_pusher.cpp
src/main/cpp/AVpacket_queue.c
src/main/cpp/media_player.c)
add_library( ffmpeg
SHARED

@ -27,19 +27,21 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--音频处理-->
<!-- 音频处理 -->
<activity android:name=".activity.AudioHandleActivity" />
<!--音视频处理-->
<!-- 音视频处理 -->
<activity android:name=".activity.MediaHandleActivity" />
<!--视频处理-->
<!-- 视频处理 -->
<activity android:name=".activity.VideoHandleActivity" />
<!--视频解码播放-->
<!-- 视频解码播放 -->
<activity
android:name=".activity.VideoPlayerActivity"
android:screenOrientation="landscape" />
<!-- 推流直播 -->
<activity android:name=".activity.PushActivity" />
<!--音视频解码播放-->
<activity android:name=".activity.MediaPlayerActivity"
android:screenOrientation="landscape"/>
<!--推流直播-->
<activity android:name=".activity.PushActivity"/>
</application>
</manifest>

@ -0,0 +1,63 @@
//
// Created by frank on 2018/2/3.
//
#include "AVpacket_queue.h"
#include <stdlib.h>
#include <libavcodec/avcodec.h>
AVPacketQueue* queue_init(int size){
AVPacketQueue* queue = malloc(sizeof(AVPacketQueue));
queue->size = size;
queue->next_to_read = 0;
queue->next_to_write = 0;
int i;
queue->packets = malloc(sizeof(*queue->packets) * size);
for(i = 0; i < size; i++){
queue->packets[i] = malloc(sizeof(AVPacket));
}
return queue;
}
void queue_free(AVPacketQueue *queue){
int i;
for(i=0; i<queue->size; i++){
free(queue->packets[i]);
}
free(queue->packets);
free(queue);
}
int queue_next(AVPacketQueue* queue, int current){
return (current+1) % queue->size;
}
void* queue_push(AVPacketQueue* queue, pthread_mutex_t* mutex, pthread_cond_t* cond){
int current = queue->next_to_write;
int next_to_write;
for(;;){
next_to_write = queue_next(queue, current);
//写的不等于读的,跳出循环
if(next_to_write != queue->next_to_read){
break;
}
pthread_cond_wait(cond, mutex);
}
queue->next_to_write = next_to_write;
pthread_cond_broadcast(cond);
return queue->packets[current];
}
void* queue_pop(AVPacketQueue* queue, pthread_mutex_t* mutex, pthread_cond_t* cond){
int current = queue->next_to_read;
for(;;){
//写的不等于读的,跳出循环
if(queue->next_to_write != queue->next_to_read){
break;
}
pthread_cond_wait(cond, mutex);
}
queue->next_to_read = queue_next(queue, current);
pthread_cond_broadcast(cond);
return queue->packets[current];
}

@ -0,0 +1,25 @@
//
// Created by frank on 2018/2/3.
//
#ifndef VIDEOPLAYER_AVPACKET_QUEUE_H
#define VIDEOPLAYER_AVPACKET_QUEUE_H
#include <pthread.h>
typedef struct AVPacketQueue{
//队列大小
int size;
//指针数组
void ** packets;
//下一个写入的packet
int next_to_write;
//下一个读取的packet
int next_to_read;
}AVPacketQueue;
AVPacketQueue* queue_init(int size);
void queue_free(AVPacketQueue *queue);
void* queue_push(AVPacketQueue* queue, pthread_mutex_t* mutex, pthread_cond_t* cond);
void* queue_pop(AVPacketQueue* queue, pthread_mutex_t* mutex, pthread_cond_t* cond);
#endif //VIDEOPLAYER_AVPACKET_QUEUE_H

@ -0,0 +1,505 @@
//
// Created by frank on 2018/2/3.
//
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "AVpacket_queue.h"
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <stdio.h>
#include <unistd.h>
#include <libavutil/imgutils.h>
#include <android/log.h>
#include <pthread.h>
#include <jni.h>
#include <libavutil/time.h>
#define TAG "MediaPlayer"
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO, TAG, FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR, TAG, FORMAT,##__VA_ARGS__);
#define MAX_AUDIO_FRAME_SIZE 48000 * 4
#define PACKET_SIZE 50
#define MIN_SLEEP_TIME_US 1000ll
#define AUDIO_TIME_ADJUST_US -200000ll
typedef struct MediaPlayer{
AVFormatContext* format_context;
int video_stream_index;
int audio_stream_index;
AVCodecContext* video_codec_context;
AVCodecContext* audio_codec_context;
AVCodec* video_codec;
AVCodec* audio_codec;
ANativeWindow* native_window;
uint8_t* buffer;
AVFrame* yuv_frame;
AVFrame* rgba_frame;
int video_width;
int video_height;
SwrContext* swrContext;
int out_channel_nb;
int out_sample_rate;
enum AVSampleFormat out_sample_fmt;
jobject audio_track;
jmethodID audio_track_write_mid;
uint8_t* audio_buffer;
AVFrame* audio_frame;
AVPacketQueue* packets[2];
pthread_mutex_t mutex;
pthread_cond_t cond;
int64_t start_time;
int64_t audio_clock;
pthread_t write_thread;
pthread_t video_thread;
pthread_t audio_thread;
}MediaPlayer;
typedef struct Decoder{
MediaPlayer* player;
int stream_index;
}Decoder;
JavaVM* javaVM;
MediaPlayer* player;
jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
javaVM = vm;
return JNI_VERSION_1_6;
}
//初始化输入格式上下文
int init_input_format_context(MediaPlayer* player, const char* file_name){
//注册所有组件
av_register_all();
//分配上下文
player->format_context = avformat_alloc_context();
//打开视频文件
if(avformat_open_input(&player->format_context, file_name, NULL, NULL)!=0) {
LOGE("Couldn't open file:%s\n", file_name);
return -1;
}
//检索多媒体流信息
if(avformat_find_stream_info(player->format_context, NULL)<0) {
LOGE("Couldn't find stream information.");
return -1;
}
//寻找音视频流索引位置
int i;
player->video_stream_index = -1;
player->audio_stream_index = -1;
for (i = 0; i < player->format_context->nb_streams; i++) {
if (player->format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO
&& player->video_stream_index < 0) {
player->video_stream_index = i;
} else if (player->format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
&& player->audio_stream_index < 0) {
player->audio_stream_index = i;
}
}
if(player->video_stream_index==-1) {
LOGE("couldn't find a video stream.");
return -1;
}
if(player->audio_stream_index==-1) {
LOGE("couldn't find a audio stream.");
return -1;
}
LOGI("video_stream_index=%d", player->video_stream_index);
LOGI("audio_stream_index=%d", player->audio_stream_index);
return 0;
}
//打开音视频解码器
int init_condec_context(MediaPlayer* player){
//获取codec上下文指针
player->video_codec_context = player->format_context->streams[player->video_stream_index]->codec;
//寻找视频流的解码器
player->video_codec = avcodec_find_decoder(player->video_codec_context->codec_id);
if(player->video_codec == NULL) {
LOGE("couldn't find video Codec.");
return -1;
}
if(avcodec_open2(player->video_codec_context, player->video_codec, NULL) < 0) {
LOGE("Couldn't open video codec.");
return -1;
}
player->audio_codec_context = player->format_context->streams[player->audio_stream_index]->codec;
player->audio_codec = avcodec_find_decoder(player->audio_codec_context->codec_id);
if( player->audio_codec == NULL) {
LOGE("couldn't find audio Codec.");
return -1;
}
if(avcodec_open2(player->audio_codec_context, player->audio_codec, NULL) < 0) {
LOGE("Couldn't open audio codec.");
return -1;
}
// 获取视频宽高
player->video_width = player->video_codec_context->width;
player->video_height = player->video_codec_context->height;
return 0;
}
//视频解码
void video_player_prepare(MediaPlayer* player, JNIEnv* env, jobject surface){
// 获取native window
player->native_window = ANativeWindow_fromSurface(env, surface);
}
//获取当前播放时间
int64_t get_play_time(MediaPlayer* player){
return (int64_t)(av_gettime() - player->start_time);
}
/**
*
*/
void player_wait_for_frame(MediaPlayer *player, int64_t stream_time) {
pthread_mutex_lock(&player->mutex);
for(;;){
int64_t current_video_time = get_play_time(player);
int64_t sleep_time = stream_time - current_video_time;
if (sleep_time < -300000ll) {
// 300 ms late
int64_t new_value = player->start_time - sleep_time;
player->start_time = new_value;
pthread_cond_broadcast(&player->cond);
}
if (sleep_time <= MIN_SLEEP_TIME_US) {
// We do not need to wait if time is slower then minimal sleep time
break;
}
if (sleep_time > 500000ll) {
// if sleep time is bigger then 500ms just sleep this 500ms
// and check everything again
sleep_time = 500000ll;
}
//等待指定时长
pthread_cond_timeout_np(&player->cond, &player->mutex,
(unsigned int) (sleep_time / 1000ll));
}
pthread_mutex_unlock(&player->mutex);
}
//视频解码
int decode_video(MediaPlayer* player, AVPacket* packet){
// 设置native window的buffer大小,可自动拉伸
ANativeWindow_setBuffersGeometry(player->native_window, player->video_width,
player->video_height, WINDOW_FORMAT_RGBA_8888);
ANativeWindow_Buffer windowBuffer;
//申请内存
player->yuv_frame = av_frame_alloc();
player->rgba_frame = av_frame_alloc();
if(player->rgba_frame == NULL || player->yuv_frame == NULL) {
LOGE("Couldn't allocate video frame.");
return -1;
}
// buffer中数据用于渲染,且格式为RGBA
int numBytes=av_image_get_buffer_size(AV_PIX_FMT_RGBA, player->video_width, player->video_height, 1);
player->buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
av_image_fill_arrays(player->rgba_frame->data, player->rgba_frame->linesize, player->buffer, AV_PIX_FMT_RGBA,
player->video_width, player->video_height, 1);
// 由于解码出来的帧格式不是RGBA的,在渲染之前需要进行格式转换
struct SwsContext *sws_ctx = sws_getContext(
player->video_width,
player->video_height,
player->video_codec_context->pix_fmt,
player->video_width,
player->video_height,
AV_PIX_FMT_RGBA,
SWS_BILINEAR,
NULL,
NULL,
NULL);
int frameFinished;
//对该帧进行解码
int ret = avcodec_decode_video2(player->video_codec_context, player->yuv_frame, &frameFinished, packet);
if(ret < 0){
LOGE("avcodec_decode_video2 error...");
return -1;
}
if (frameFinished) {
// lock native window
ANativeWindow_lock(player->native_window, &windowBuffer, 0);
// 格式转换
sws_scale(sws_ctx, (uint8_t const * const *)player->yuv_frame->data,
player->yuv_frame->linesize, 0, player->video_height,
player->rgba_frame->data, player->rgba_frame->linesize);
// 获取stride
uint8_t * dst = windowBuffer.bits;
int dstStride = windowBuffer.stride * 4;
uint8_t * src = player->rgba_frame->data[0];
int srcStride = player->rgba_frame->linesize[0];
// 由于window的stride和帧的stride不同,因此需要逐行复制
int h;
for (h = 0; h < player->video_height; h++) {
memcpy(dst + h * dstStride, src + h * srcStride, (size_t) srcStride);
}
//计算延迟
int64_t pts = av_frame_get_best_effort_timestamp(player->yuv_frame);
AVStream *stream = player->format_context->streams[player->video_stream_index];
//转换(不同时间基时间转换)
int64_t time = av_rescale_q(pts, stream->time_base, AV_TIME_BASE_Q);
//音视频帧同步
player_wait_for_frame(player, time);
ANativeWindow_unlockAndPost(player->native_window);
}
// //延迟等待
// usleep(1000 * 16);
return 0;
}
//音频解码初始化
void audio_decoder_prepare(MediaPlayer* player) {
//frame->16bit 44100 PCM 统一音频采样格式与采样率
player->swrContext = swr_alloc();
//输入的采样格式
enum AVSampleFormat in_sample_fmt = player->audio_codec_context->sample_fmt;
//输出采样格式16bit PCM
player->out_sample_fmt = AV_SAMPLE_FMT_S16;
//输入采样率
int in_sample_rate = player->audio_codec_context->sample_rate;
//输出采样率
player->out_sample_rate = in_sample_rate;
//声道布局(2个声道,默认立体声stereo)
uint64_t in_ch_layout = player->audio_codec_context->channel_layout;
//输出的声道布局(立体声)
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
swr_alloc_set_opts(player->swrContext,
out_ch_layout, player->out_sample_fmt, player->out_sample_rate,
in_ch_layout, in_sample_fmt, in_sample_rate,
0, NULL);
swr_init(player->swrContext);
//输出的声道个数
player->out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
}
//音频播放器
void audio_player_prepare(MediaPlayer* player, JNIEnv* env, jclass jthiz){
jclass player_class = (*env)->GetObjectClass(env,jthiz);
if(!player_class){
LOGE("player_class not found...");
}
//AudioTrack对象
jmethodID audio_track_method = (*env)->GetMethodID(
env,player_class,"createAudioTrack","(II)Landroid/media/AudioTrack;");
if(!audio_track_method){
LOGE("audio_track_method not found...");
}
jobject audio_track = (*env)->CallObjectMethod(
env,jthiz,audio_track_method, player->out_sample_rate, player->out_channel_nb);
//调用play方法
jclass audio_track_class = (*env)->GetObjectClass(env, audio_track);
jmethodID audio_track_play_mid = (*env)->GetMethodID(env,audio_track_class,"play","()V");
(*env)->CallVoidMethod(env, audio_track, audio_track_play_mid);
player->audio_track = (*env)->NewGlobalRef(env, audio_track);
//获取write()方法
player->audio_track_write_mid = (*env)->GetMethodID(env,audio_track_class,"write","([BII)I");
//16bit 44100 PCM 数据
player->audio_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE);
//解压缩数据
player->audio_frame = av_frame_alloc();
}
//音频解码
int decode_audio(MediaPlayer* player, AVPacket* packet){
int got_frame = 0, ret;
//解码
ret = avcodec_decode_audio4(player->audio_codec_context, player->audio_frame, &got_frame, packet);
if(ret < 0){
LOGE("avcodec_decode_audio4 error...");
return -1;
}
//解码一帧成功
if(got_frame > 0){
//音频格式转换
swr_convert(player->swrContext, &player->audio_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)player->audio_frame->data, player->audio_frame->nb_samples);
int out_buffer_size = av_samples_get_buffer_size(NULL, player->out_channel_nb,
player->audio_frame->nb_samples, player->out_sample_fmt, 1);
//音视频帧同步
int64_t pts = packet->pts;
if (pts != AV_NOPTS_VALUE) {
AVStream *stream = player->format_context->streams[player->audio_stream_index];
player->audio_clock = av_rescale_q(pts, stream->time_base, AV_TIME_BASE_Q);
player_wait_for_frame(player, player->audio_clock + AUDIO_TIME_ADJUST_US);
}
JNIEnv * env;
(*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
jbyteArray audio_sample_array = (*env)->NewByteArray(env,out_buffer_size);
jbyte* sample_byte_array = (*env)->GetByteArrayElements(env,audio_sample_array,NULL);
//拷贝缓冲数据
memcpy(sample_byte_array, player->audio_buffer, (size_t) out_buffer_size);
//释放数组
(*env)->ReleaseByteArrayElements(env,audio_sample_array,sample_byte_array,0);
//调用AudioTrack的write方法进行播放
(*env)->CallIntMethod(env, player->audio_track, player->audio_track_write_mid,
audio_sample_array,0,out_buffer_size);
//释放局部引用
(*env)->DeleteLocalRef(env,audio_sample_array);
// usleep(1000 * 16);
}
(*javaVM)->DetachCurrentThread(javaVM);
return 0;
}
//初始化队列
void init_queue(MediaPlayer* player, int size){
int i;
for (i = 0; i < 2; ++i) {
AVPacketQueue* queue = queue_init(size);
player->packets[i] = queue;
}
}
//释放队列
void delete_queue(MediaPlayer* player){
int i;
for (i = 0; i < 2; ++i) {
queue_free(player->packets[i]);
}
}
//读取AVPacket线程(生产者)
void* write_packet_to_queue(void* arg){
MediaPlayer* player = (MediaPlayer*)arg;
AVPacket packet, *pkt = &packet;
int ret;
for(;;){
ret = av_read_frame(player->format_context, pkt);
if(ret < 0){
break;
}
if(pkt->stream_index == player->video_stream_index || pkt->stream_index == player->audio_stream_index){
//根据AVPacket->stream_index获取对应的队列
AVPacketQueue *queue = player->packets[pkt->stream_index];
pthread_mutex_lock(&player->mutex);
AVPacket* data = queue_push(queue, &player->mutex, &player->cond);
pthread_mutex_unlock(&player->mutex);
//拷贝(间接赋值,拷贝结构体数据)
*data = packet;
}
}
}
//音视频解码线程(消费者)
void* decode_func(void* arg){
Decoder *decoder_data = (Decoder*)arg;
MediaPlayer *player = decoder_data->player;
int stream_index = decoder_data->stream_index;
//根据stream_index获取对应的AVPacket队列
AVPacketQueue *queue = player->packets[stream_index];
int ret = 0;
int video_frame_count = 0, audio_frame_count = 0;
for(;;) {
pthread_mutex_lock(&player->mutex);
AVPacket *packet = (AVPacket*)queue_pop(queue, &player->mutex, &player->cond);
pthread_mutex_unlock(&player->mutex);
if(stream_index == player->video_stream_index) {//视频流
ret = decode_video(player, packet);
LOGI("decode video stream = %d", video_frame_count++);
} else if(stream_index == player->audio_stream_index) {//音频流
ret = decode_audio(player, packet);
LOGI("decode audio stream = %d", audio_frame_count++);
}
av_packet_unref(packet);
if(ret < 0){
break;
}
}
}
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_MediaPlayer_setup
(JNIEnv * env, jclass clazz, jstring filePath, jobject surface){
const char *file_name = (*env)->GetStringUTFChars(env, filePath, JNI_FALSE);
int ret;
player = malloc(sizeof(MediaPlayer));
if(player == NULL){
return -1;
}
//初始化输入格式上下文
ret = init_input_format_context(player, file_name);
if(ret < 0){
return ret;
}
//初始化音视频解码器
ret = init_condec_context(player);
if(ret < 0){
return ret;
}
//初始化视频surface
video_player_prepare( player, env, surface);
//初始化音频相关参数
audio_decoder_prepare(player);
//初始化音频播放器
audio_player_prepare(player, env, clazz);
//初始化音视频packet队列
init_queue(player, PACKET_SIZE);
return 0;
}
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_MediaPlayer_play
(JNIEnv * env, jclass clazz){
pthread_mutex_init(&player->mutex, NULL);
pthread_cond_init(&player->cond, NULL);
//生产者线程
pthread_create(&player->write_thread, NULL, write_packet_to_queue, (void*)player);
sleep(1);
player->start_time = 0;
//消费者线程
Decoder data1 = {player, player->video_stream_index}, *decoder_data1 = &data1;
pthread_create(&player->video_thread, NULL, decode_func, (void*)decoder_data1);
Decoder data2 = {player, player->audio_stream_index}, *decoder_data2 = &data2;
pthread_create(&player->audio_thread,NULL,decode_func,(void*)decoder_data2);
pthread_join(player->write_thread, NULL);
pthread_join(player->video_thread, NULL);
pthread_join(player->audio_thread, NULL);
return 0;
}
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_MediaPlayer_release
(JNIEnv * env, jclass clazz){
//释放内存以及关闭文件
free(player->audio_track);
free(player->audio_track_write_mid);
av_free(player->buffer);
av_free(player->rgba_frame);
av_free(player->yuv_frame);
av_free(player->audio_buffer);
av_free(player->audio_frame);
avcodec_close(player->video_codec_context);
avcodec_close(player->audio_codec_context);
avformat_close_input(&player->format_context);
ANativeWindow_release(player->native_window);
delete_queue(player);
pthread_cond_destroy(&player->cond);
pthread_mutex_destroy(&player->mutex);
free(player);
(*javaVM)->DestroyJavaVM(javaVM);
}

@ -0,0 +1,45 @@
package com.frank.ffmpeg;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
/**
* 音视频播放器
* Created by frank on 2018/2/12.
*/
public class MediaPlayer {
static {
System.loadLibrary("media-handle");
}
public native int setup(String filePath, Object surface);
public native int play();
public native void release();
/**
* 创建一个AudioTrack对象
* @param sampleRate 采样率
* @param channels 声道布局
* @return AudioTrack
*/
public AudioTrack createAudioTrack(int sampleRate, int channels){
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int channelConfig;
if(channels == 1){
channelConfig = android.media.AudioFormat.CHANNEL_OUT_MONO;
}else if(channels == 2){
channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;
}else{
channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;
}
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
return new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat,
bufferSizeInBytes, AudioTrack.MODE_STREAM);
}
}

@ -4,6 +4,8 @@ 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;
/**
@ -22,9 +24,11 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
private void initView() {
findViewById(R.id.btn_audio).setOnClickListener(this);
findViewById(R.id.btn_media).setOnClickListener(this);
findViewById(R.id.btn_video).setOnClickListener(this);
findViewById(R.id.btn_media).setOnClickListener(this);
findViewById(R.id.btn_play).setOnClickListener(this);
findViewById(R.id.btn_push).setOnClickListener(this);
findViewById(R.id.btn_live).setOnClickListener(this);
}
@Override
@ -33,15 +37,22 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
case R.id.btn_audio://音频处理
startActivity(new Intent(MainActivity.this, AudioHandleActivity.class));
break;
case R.id.btn_video://视频处理
startActivity(new Intent(MainActivity.this, VideoHandleActivity.class));
break;
case R.id.btn_media://音视频处理
startActivity(new Intent(MainActivity.this, MediaHandleActivity.class));
break;
case R.id.btn_video://视频处理
startActivity(new Intent(MainActivity.this, VideoHandleActivity.class));
case R.id.btn_play://音视频播放
startActivity(new Intent(MainActivity.this, MediaPlayerActivity.class));
break;
case R.id.btn_push://FFmpeg推流
startActivity(new Intent(MainActivity.this, PushActivity.class));
break;
case R.id.btn_live://实时推流直播:AAC音频编码、H264视频编码、RTMP推流
Toast.makeText(MainActivity.this, "暂时没该功能...", Toast.LENGTH_SHORT).show();
// startActivity(new Intent(MainActivity.this, PushActivity.class));
break;
default:
break;
}

@ -0,0 +1,81 @@
package com.frank.ffmpeg.activity;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.frank.ffmpeg.MediaPlayer;
import com.frank.ffmpeg.R;
import java.io.File;
/**
* 音视频解码播放
* Created by frank on 2018/2/12.
*/
public class MediaPlayerActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private static final String TAG = MediaPlayerActivity.class.getSimpleName();
SurfaceHolder surfaceHolder;
private final static String PATH = Environment.getExternalStorageDirectory().getPath() + File.separator;
private String filePath = PATH + "hello.mp4";
private MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_media_player);
initView();
initPlayer();
}
private void initView(){
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface_media);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
// Button btn_slow = (Button) findViewById(R.id.btn_play_slow);
// Button btn_fast = (Button) findViewById(R.id.btn_play_fast);
}
private void initPlayer(){
mediaPlayer = new MediaPlayer();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated");
new Thread(new Runnable() {
@Override
public void run() {
int result = mediaPlayer.setup(filePath, surfaceHolder.getSurface());
if(result < 0){
Log.e(TAG, "mediaPlayer-->setup");
return;
}
mediaPlayer.play();
}
}).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed");
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mediaPlayer != null){
mediaPlayer.release();
mediaPlayer = null;
}
}
}

@ -19,7 +19,7 @@ import java.io.File;
* Created by frank on 2018/2/1.
*/
public class VideoPlayerActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String TAG = VideoPlayerActivity.class.getSimpleName();
SurfaceHolder surfaceHolder;
private final static String PATH = Environment.getExternalStorageDirectory().getPath() + File.separator;
private String filePath = PATH + "hello.mp4";

@ -53,7 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_play"
android:layout_below="@+id/btn_concat"
android:layout_below="@+id/btn_mix"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"/>

@ -11,6 +11,15 @@
android:layout_centerHorizontal="true"
android:text="@string/audio_handle"/>
<Button
android:id="@+id/btn_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_handle"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_audio"/>
<Button
android:id="@+id/btn_media"
android:layout_width="wrap_content"
@ -18,13 +27,13 @@
android:text="@string/media_handle"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_audio"/>
android:layout_below="@+id/btn_video"/>
<Button
android:id="@+id/btn_video"
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_handle"
android:text="@string/media_play"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_media"/>
@ -36,6 +45,15 @@
android:text="@string/video_push"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_video"/>
android:layout_below="@+id/btn_play"/>
<Button
android:id="@+id/btn_live"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video_live"
android:layout_marginTop="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/btn_push"/>
</RelativeLayout>

@ -0,0 +1,33 @@
<?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.MediaPlayerActivity">
<SurfaceView
android:id="@+id/surface_media"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/btn_play_slow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_margin="16dp"
android:text="@string/video_slow"
android:visibility="gone"/>
<Button
android:id="@+id/btn_play_fast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_margin="16dp"
android:text="@string/video_fast"
android:visibility="gone"/>
</RelativeLayout>

@ -10,11 +10,13 @@
<string name="audio_handle">音频处理</string>
<string name="media_handle">音视频处理</string>
<string name="video_handle">视频处理</string>
<string name="video_push">推流直播</string>
<string name="video_push">本地推流直播</string>
<string name="video_live">实时推流直播</string>
<string name="media_mux">音视频合成</string>
<string name="media_extra_audio">提取音频</string>
<string name="media_extract_video">提取视频</string>
<string name="media_play">音视频播放</string>
<string name="video_cut">视频剪切</string>
<string name="video_concat">视频拼接</string>

Loading…
Cancel
Save