ffmpegandroid使用
‘壹’ 如何在Android用FFmpeg解码图像
创建一个VideoPicture结构体用来保存解码出来的图像。
LOCAL_PATH := $(call my-dir)
###########################
#
# SDL shared library
#
###########################
include $(CLEAR_VARS)
LOCAL_MODULE := SDL2
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \
$(subst $(LOCAL_PATH)/,, \
$(wildcard $(LOCAL_PATH)/src/*.c) \
$(wildcard $(LOCAL_PATH)/src/audio/*.c) \
$(wildcard $(LOCAL_PATH)/src/audio/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/audio/mmy/*.c) \
$(LOCAL_PATH)/src/atomic/SDL_atomic.c \
$(LOCAL_PATH)/src/atomic/SDL_spinlock.c.arm \
$(wildcard $(LOCAL_PATH)/src/core/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/cpuinfo/*.c) \
$(wildcard $(LOCAL_PATH)/src/dynapi/*.c) \
$(wildcard $(LOCAL_PATH)/src/events/*.c) \
$(wildcard $(LOCAL_PATH)/src/file/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/mmy/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/filesystem/mmy/*.c) \
$(wildcard $(LOCAL_PATH)/src/render/*.c) \
$(wildcard $(LOCAL_PATH)/src/render/*/*.c) \
$(wildcard $(LOCAL_PATH)/src/stdlib/*.c) \
$(wildcard $(LOCAL_PATH)/src/thread/*.c) \
$(wildcard $(LOCAL_PATH)/src/thread/pthread/*.c) \
$(wildcard $(LOCAL_PATH)/src/timer/*.c) \
$(wildcard $(LOCAL_PATH)/src/timer/unix/*.c) \
$(wildcard $(LOCAL_PATH)/src/video/*.c) \
$(wildcard $(LOCAL_PATH)/src/video/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/test/*.c))
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog -landroid
include $(BUILD_SHARED_LIBRARY)
###########################
#
# SDL static library
#
###########################
#LOCAL_MODULE := SDL2_static
#LOCAL_MODULE_FILENAME := libSDL2
#LOCAL_SRC_FILES += $(LOCAL_PATH)/src/main/android/SDL_android_main.c
#LOCAL_LDLIBS :=
#LOCAL_EXPORT_LDLIBS := -Wl,--undefined=java_org_libsdl_app_SDLActivity_nativeInit -ldl -lGLESv1_CM -lGLESv2 -llog -landroid
#include $(BUILD_STATIC_LIBRARY)
二、参考[原]如何在Android用FFmpeg解码图像, 在工程中新建一个ffmpeg文件夹,将与ffmpeg相关的头文件include进来。ffmpeg文件夹下的Android.mk内容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := /path/to/build/ffmpeg/libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)
三、新建player文件夹,用来编写解码与显示文件。player.c文件内容:
/*
* SDL_Lesson.c
*
* Created on: Aug 12, 2014
* Author: clarck
*/
#include <jni.h>
#include <android/native_window_jni.h>
#include "SDL.h"
#include "SDL_thread.h"
#include "SDL_events.h"
#include "../include/logger.h"
#include "../ffmpeg/include/libavcodec/avcodec.h"
#include "../ffmpeg/include/libavformat/avformat.h"
#include "../ffmpeg/include/libavutil/pixfmt.h"
#include "../ffmpeg/include/libswscale/swscale.h"
int main(int argc, char *argv[]) {
char *file_path = argv[1];
LOGI("file_path:%s", file_path);
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame, *pFrameYUV;
AVPacket *packet;
uint8_t *out_buffer;
SDL_Texture *bmp = NULL;
SDL_Window *screen = NULL;
SDL_Rect rect;
SDL_Event event;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes;
int ret, got_picture;
av_register_all();
pFormatCtx = avformat_alloc_context();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
LOGE("Could not initialize SDL - %s. \n", SDL_GetError());
exit(1);
}
if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {
LOGE("can't open the file. \n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
LOGE("Could't find stream infomation.\n");
return -1;
}
videoStream = 1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
}
LOGI("videoStream:%d", videoStream);
if (videoStream == -1) {
LOGE("Didn't find a video stream.\n");
return -1;
}
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
LOGE("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
LOGE("Could not open codec.\n");
return -1;
}
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
//---------------------------init sdl---------------------------//
screen = SDL_CreateWindow("My Player Window", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width, pCodecCtx->height,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
SDL_Renderer *renderer = SDL_CreateRenderer(screen, -1, 0);
bmp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
//-------------------------------------------------------------//
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
pCodecCtx->width, pCodecCtx->height);
rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket));
av_new_packet(packet, y_size);
av_mp_format(pFormatCtx, 0, file_path, 0);
while (av_read_frame(pFormatCtx, packet) >= 0) {
if (packet->stream_index == videoStream) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
packet);
if (ret < 0) {
LOGE("decode error.\n");
return -1;
}
LOGI("got_picture:%d", got_picture);
if (got_picture) {
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data,
pFrameYUV->linesize);
////iPitch 计算yuv一行数据占的字节数
SDL_UpdateTexture(bmp, &rect, pFrameYUV->data[0], pFrameYUV->linesize[0]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, bmp, &rect, &rect);
SDL_RenderPresent(renderer);
}
SDL_Delay(50);
}
av_free_packet(packet);
SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
SDL_Quit();
exit(0);
break;
default:
break;
}
‘贰’ 如何在Android用FFmpeg解码图像
fetch code
用git把ffmpeg(我用的github上FFmpeg-Android)和x264(vlc的官方git)分别都clone下来。
build x264
在x264目录里面写一个myconfig.sh(其实直接把这些命令打在终端也行,问题是有的时候需要改来改去,不如写个文件改起来方便)
export NDK=/opt/android-ndk
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt
export PLATFORM=$NDK/platforms/android-14/arch-arm
export PREFIX=/home/mingkai/softwares/x264
./configure \
--enable-pic \
--enable-static \
--enable-shared \
--disable-asm \
--disable-cli \
--host=arm-linux \
--cross-prefix="/opt/android-ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/arm-linux-androideabi-"
\
--sysroot=$PLATFORM \
--prefix=$PREFIX
其中prefix貌似直接用"arm-linux-androideabi-“也可以。
然后可以make和make install了。(记得改PREFIX等环境变量)
build FFmpeg
这个是从github上FFmpeg-Android里面的FFmpeg-Android.sh里面改了改一些参数。
最主要的是FFMPEG_FLAGS,里面都是一些关于FFmpeg的参数设定,尤其是是否启用encoder/decoder之类的。
还有一点就是再下面EXTRA_CFLAGS里面加上
“-I/path/to/x264/include”
EXTRA_LDFLAGS里面加上
“-L/path/to/x264/lib -lx264”。
‘叁’ 如何用Android NDK编译FFmpeg
Android NDK编译FFmpeg可以采用cygwin方法来实现。
具体步骤:
首先是config脚本,编译ffmpeg之前必须得先configure一下,configure是一个shell脚本,根据命令行参数不同来裁剪模块,生成特定的config.h文件。
confiure脚本文件在ffmpeg目录里可以找到。
重新建立以个shell脚本文件config.sh,这个文件只是为了编译方便。
例子:
注意:
编写config脚本时候,其中的路径需要使用windows形式的路径,不能使用/cygwindriver/d/android 这种格式的路径。
‘肆’ 如何在Android用FFmpeg解码图像
在Android用FFmpeg解码图像
for(i=0;i<pFormatCtx->nb_streams;i++)if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoStream=i;break;}if(videoStream==-1)return-1;//Didn'tfindavideostream//=pFormatCtx->streams[videoStream]->codec;//=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){fprintf(stderr,"Unsupportedcodec! ");return-1;//Codecnotfound}//Opencodecif(avcodec_open2(pCodecCtx,pCodec,NULL)<0)pFrame=avcodec_alloc_frame();//_alloc(&picture,PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);//_convert_ctx=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,PIX_FMT_RGB24,SWS_BICUBIC,NULL,NULL,NULL);if(img_convert_ctx==NULL){fprintf(stderr,"! ");exit(1);}///*########################################[4]########################################*/i=0;dirtyRegion.set(android::Rect(0x3FFF,0x3FFF));while(av_read_frame(pFormatCtx,&packet)>=0){//?if(packet.stream_index==videoStream){//Decodevideoframeavcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);//Didwegetavideoframe?if(frameFinished){//_scale(img_convert_ctx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,picture.data,picture.linesize);}}//_read_frameav_free_packet(&packet);}pFrame为解码后的数据,将它显示在画布上,就完成了FFMEPG解码
‘伍’ 如何在Android用FFmpeg解码图像
创建一个VideoPicture结构体用来保存解码出来的图像。
/*
*SDL_Lesson.c
*
*Createdon:Aug12,2014
*Author:clarck
*/
#include<jni.h>
#include<android/native_window_jni.h>
#include"SDL.h"
#include"SDL_thread.h"
#include"SDL_events.h"
#include"../include/logger.h"
#include"../ffmpeg/include/libavcodec/avcodec.h"
#include"../ffmpeg/include/libavformat/avformat.h"
#include"../ffmpeg/include/libavutil/pixfmt.h"
#include"../ffmpeg/include/libswscale/swscale.h"
#include"../ffmpeg/include/libswresample/swresample.h"
#defineSDL_AUDIO_BUFFER_SIZE1024
#defineMAX_AUDIO_SIZE(5*16*1024)
#defineMAX_VIDEO_SIZE(5*256*1024)
#defineFF_ALLOC_EVENT(SDL_USEREVENT)
#defineFF_REFRESH_EVENT(SDL_USEREVENT+1)
#defineFF_QUIT_EVENT(SDL_USEREVENT+2)
#defineVIDEO_PICTURE_QUEUE_SIZE1
#defineAVCODEC_MAX_AUDIO_FRAME_SIZE192000//1secondof48khz32bitaudio
typedefstructPacketQueue{
AVPacketList*first_pkt,*last_pkt;
intnb_packets;
intsize;
SDL_mutex*mutex;
SDL_cond*cond;
}PacketQueue;
typedefstructVideoPicture{
SDL_Window*screen;
SDL_Renderer*renderer;
SDL_Texture*bmp;
AVFrame*rawdata;
intwidth,height;/*sourceheight&width*/
intallocated;
}VideoPicture;
typedefstructVideoState{
charfilename[1024];
AVFormatContext*ic;
intvideoStream,audioStream;
AVStream*audio_st;
AVFrame*audio_frame;
PacketQueueaudioq;
unsignedintaudio_buf_size;
unsignedintaudio_buf_index;
AVPacketaudio_pkt;
uint8_t*audio_pkt_data;
intaudio_pkt_size;
uint8_t*audio_buf;
DECLARE_ALIGNED(16,uint8_t,audio_buf2)[AVCODEC_MAX_AUDIO_FRAME_SIZE*4];
enumAVSampleFormataudio_src_fmt;
enumAVSampleFormataudio_tgt_fmt;
intaudio_src_channels;
intaudio_tgt_channels;
int64_taudio_src_channel_layout;
int64_taudio_tgt_channel_layout;
intaudio_src_freq;
intaudio_tgt_freq;
structSwrContext*swr_ctx;
AVStream*video_st;
PacketQueuevideoq;
VideoPicturepictq[VIDEO_PICTURE_QUEUE_SIZE];
intpictq_size,pictq_rindex,pictq_windex;
SDL_mutex*pictq_mutex;
SDL_cond*pictq_cond;
SDL_Thread*parse_tid;
SDL_Thread*audio_tid;
SDL_Thread*video_tid;
AVIOContext*io_ctx;
structSwsContext*sws_ctx;
intquit;
}VideoState;
VideoState*global_video_state;
‘陆’ 怎么利用ffmpeg实现android播放器
下面把具体编译步骤描述如下,假定NDK安装在~/android-ndk-r7:
1. 首先从FFmpeg官网下载最新的release版本源码ffmpeg-0.11.tar.gz解压缩到Android源码树的ffmpeg/下。
2 准备一个编译脚本build_android.sh并放在ffmpeg/下面,这个脚本也是Rockplayer提供的,需做一些修改,其内容附在后面。我目前用的也会附在后面。
3 在ffmpeg目录下运行./build_android.sh开始编译FFmpeg,编译好的libffmpeg.so会放在文件夹android里面,一共有3个版本分别对应3种ARM体系结构,包括armv7-a、armv7-a-vfp、armv6_vfp,根据所运行的硬件平台选取其中一个版本。为了编译使用FFmpeg的程序时可以方便地找到libffmpeg.so,可将它复制到$OUT/system/lib/和$OUT/obj/lib/,当然这一步也可以加在build_android.sh中做。
4. 接下来就是编译可执行文件ffmpeg了,这个工具可以在命令行下完成FFmpeg提供的几乎所有功能包括编码、解码、转码等,也是用来调试和验证很有用的工具。其实上述编译完后在$ANDROID_BUILD_TOP/external/ffmpeg/下也会生成ffmpeg,但是在设备上无法运行。为了编出能在设备上运行的ffmpeg,可以写一个简单的Android.mk,
‘柒’ android-ffmpeg-x264 怎么用
Android内置的编解码器实在太少,于是我们需要FFmpeg。Android提供了NDK,为我们使用FFmpeg这种C语言代码提供了方便。
不过为了用NDK编译FFmpeg,还真的花费了不少时间,也得到了很多人的帮助,最应该谢谢havlenapetr。我觉得我现在这些方法算是比较简洁的了--
下面就尽量详细的说一下我是怎么在项目中使用FFmpeg的,但是基于我混乱的表达能力,有不明白的就问我。
你得了解JNI和Android NDK的基本用法,若觉得我的文章还不错,可以看之前写的JNI简单入门和Android NDK入门
首先创建一个标准的Android项目vPlayer
android create project -n vPlayer -t 8 -p vPlayer -k me.abitno.vplayer -a PlayerView
然后在vPlayer目录里
mkdir jni && cd jni
wget http://ffmpeg.org/releases/ffmpeg-0.6.tar.bz2
tar xf ffmpeg-0.6.tar.bz2 && mv ffmpeg-0.6 ffmpeg && cd ffmpeg
在ffmpeg下新建一个config.sh,内容如下,注意把PREBUILT和PLATFORM设置正确。另外里面有些参数你也可以自行调整,我主要是为了配置一个播放器而这样设置的。
#!/bin/bash
PREBUILT=/home/abitno/Android/android-ndk-r4/build/prebuilt/linux-x86/arm-eabi-4.4.0
PLATFORM=/home/abitno/Android/android-ndk-r4/build/platforms/android-8/arch-arm
./configure --target-os=linux \
--arch=arm \
--enable-version3 \
--enable-gpl \
--enable-nonfree \
--disable-stripping \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffserver \
--disable-ffprobe \
--disable-encoders \
--disable-muxers \
--disable-devices \
--disable-protocols \
--enable-protocol=file \
--enable-avfilter \
--disable-network \
--disable-mpegaudio-hp \
--disable-avdevice \
--enable-cross-compile \
--cc=$PREBUILT/bin/arm-eabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-eabi- \
--nm=$PREBUILT/bin/arm-eabi-nm \
--extra-cflags="-fPIC -DANDROID" \
--disable-asm \
--enable-neon \
--enable-armv5te \
--extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"
运行config.sh开始configure
chmod +x config.sh
./config.sh
configure完成后,编辑刚刚生成的config.h,找到这句
#define restrict restrict
Android的GCC不支持restrict关键字,于是修改成下面这样
#define restrict
编辑libavutil/libm.h,把其中的static方法都删除。
‘捌’ 怎么配置.Android使用ffmpeg的swscale等函数
创建一个VideoPicture结构体用来保存解码出来的图像。 /* * SDL_Lesson.c * * Created on: Aug 12, 2014 * Author: clarck */ #include <jni.h> #include <android/native_window_jni.h> #include "SDL.h" #include "SDL_thread.h" #include "SDL_events.h" #include "../include/logger.h" #include "../ffmpeg/include/libavcodec/avcodec.h" #include "../ffmpeg/include/libavformat/avformat.h" #include "../ffmpeg/include/libavutil/pixfmt.h" #include "../ffmpeg/include/libswscale/swscale.h" #include "../ffmpeg/include/libswresample/swresample.h" #define SDL_AUDIO_BUFFER_SIZE 1024 #define MAX_AUDIO_SIZE (5 * 16 * 1024) #define MAX_VIDEO_SIZE (5 * 256 * 1024) #define FF_ALLOC_EVENT (SDL_USEREVENT) #define FF_REFRESH_EVENT (SDL_USEREVENT + 1) #define FF_QUIT_EVENT (SDL_USEREVENT + 2) #define VIDEO_PICTURE_QUEUE_SIZE 1 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; SDL_mutex *mutex; SDL_cond *cond; } PacketQueue; typedef struct VideoPicture { SDL_Window *screen; SDL_Renderer *renderer; SDL_Texture *bmp; AVFrame* rawdata; int width, height; /*source height & width*/ int allocated; } VideoPicture; typedef struct VideoState { char filename[1024]; AVFormatContext *ic; int videoStream, audioStream; AVStream *audio_st; AVFrame *audio_frame; PacketQueue audioq; unsigned int audio_buf_size; unsigned int audio_buf_index; AVPacket audio_pkt; uint8_t *audio_pkt_data; int audio_pkt_size; uint8_t *audio_buf; DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4]; enum AVSampleFormat audio_src_fmt; enum AVSampleFormat audio_tgt_fmt; int audio_src_channels; int audio_tgt_channels; int64_t audio_src_channel_layout; int64_t audio_tgt_channel_layout; int audio_src_freq; int audio_tgt_freq; struct SwrContext *swr_ctx; AVStream *video_st; PacketQueue videoq; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; int pictq_size, pictq_rindex, pictq_windex; SDL_mutex *pictq_mutex; SDL_cond *pictq_cond; SDL_Thread *parse_tid; SDL_Thread *audio_tid; SDL_Thread *video_tid; AVIOContext *io_ctx; struct SwsContext *sws_ctx; int quit; } VideoState; VideoState *global_video_state; 如果我的回答没能帮助您,请继续追问。 转载,仅供参考。
‘玖’ android 中使用ffmpeg,将视频加入字幕,用ass文件,在PC上可以,但在Android上一直不行
理论上来讲:任何视频格式都是支持外挂字幕的 迅雷看看也是支持外挂字幕的 没显示字幕的可能性有两个: 1、播放器不支持外挂字幕(显然应该不是播放器的问题,有可能你关闭了外挂字幕功能,你可以试着重新安装迅雷看看) 2、视频文件名跟字幕文件名不同(在同一个文件夹下要播放器自动载入外挂字幕,首先必须保证视频跟字幕在同一个文件夹内,然后要保证文件名称一样,这里说的文件名称不包括后缀名 举个例子: 视频文件名为:ZXCV.mp4 ,,这里我们可以看到这个视频是一个名称为:ZXCV的MP4格式的视频文件,ZXCV为为视频名称,“.mp4”则为文件的后缀名,即为文件种类 要保证外挂字幕文件顺利的自动载入,那么它的文件名就必须为:“ZXCV.ass”) 明白?
‘拾’ android ffmpeg怎么用
这个是一个大项,专业人员都要学两三天才能下手,所以我就简述一下
1、最简单你可以使用现成的,很多基于ffmpeg封装的框架 (ijkPlayer
GSYVideoPlayer)
2、自己做 需要有JNI知识(包含基本C/C++语法)给你看下大致流程
其中so库也可以网上下载到
3、ffmpeg参考文献基本都是推雷霄骅的,但是他不幸GG了,所以文献什么的都有点过时,我们使用AS开发有很多便利了,他都没讲到,可以网络过滤获取最新的视屏资源和文章学习