中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

奶茶加盟網(wǎng)站建設(shè)外鏈網(wǎng)盤

奶茶加盟網(wǎng)站建設(shè),外鏈網(wǎng)盤,網(wǎng)站開發(fā)需要什么人員,WordPress完全隱藏特定分類音視頻介紹 音視頻解碼流程 FFmpeg解碼的數(shù)據(jù)結(jié)構(gòu)說明 AVFormatContext:封裝格式上下文結(jié)構(gòu)體,全局結(jié)構(gòu)體,保存了視頻文件封裝格式相關(guān)信息AVInputFormat:每種封裝格式,對應(yīng)一個(gè)該結(jié)構(gòu)體AVStream[0]:視頻文件中每個(gè)視頻&#xff…

音視頻介紹

音視頻解碼流程

在這里插入圖片描述

FFmpeg解碼的數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • AVFormatContext:封裝格式上下文結(jié)構(gòu)體,全局結(jié)構(gòu)體,保存了視頻文件封裝格式相關(guān)信息
  • AVInputFormat:每種封裝格式,對應(yīng)一個(gè)該結(jié)構(gòu)體
  • AVStream[0]:視頻文件中每個(gè)視頻(音頻)流對應(yīng)一個(gè)該結(jié)構(gòu)體
  • AVCodecContext:編碼器上下文結(jié)構(gòu)體,保存了視頻(音頻)編解碼相關(guān)信息
  • AVCodec:每種視頻(音頻)編解碼器(例如H.264解碼器)對應(yīng)一個(gè)該結(jié)構(gòu)體

AVFormatContext數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • iformat:輸入視頻的AVInputFormat
  • nb_streams:輸入視頻的AVStream 個(gè)數(shù)
  • streams:輸入視頻的AVStream []數(shù)組
  • duration:輸入視頻的時(shí)長(以微秒為單位)
  • bit_rate:輸入視頻的碼率

AVInputFormat數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • name:封裝格式名稱
  • long_name:封裝格式的長名稱
  • extensions:封裝格式的擴(kuò)展名
  • id:封裝格式ID
  • 一些封裝格式處理的接口函數(shù)

AVStream數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • id:序號
  • codec:該流對應(yīng)的AVCodecContext
  • time_base:該流的時(shí)基
  • avg_frame_rate:該流的幀率

AVCodecContext數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • codec:編解碼器的AVCodec
  • width, height:圖像的寬高
  • pix_fmt:像素格式
  • sample_rate:音頻采樣率
  • channels:聲道數(shù)
  • sample_fmt:音頻采樣格式

AVCodec數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • name:編解碼器名稱
  • long_name:編解碼器長名稱
  • type:編解碼器類型
  • id:編解碼器ID
  • 一些編解碼的接口函數(shù)

AVPacket數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • pts:顯示時(shí)間戳
  • dts:解碼時(shí)間戳
  • data:壓縮編碼數(shù)據(jù)
  • size:壓縮編碼數(shù)據(jù)大小
  • stream_index:所屬的AVStream

AVFrame數(shù)據(jù)結(jié)構(gòu)說明

在這里插入圖片描述

  • data:解碼后的圖像像素?cái)?shù)據(jù)(音頻采樣數(shù)據(jù))
  • linesize:對視頻來說是圖像中一行像素的大小;對音頻來說是整個(gè)音
  • width, height:圖像的寬高
  • key_frame:是否為關(guān)鍵幀
  • pict_type:幀類型(只針對視頻) 。例如I,P,B

音視頻實(shí)戰(zhàn)

將編譯好的FFmpeg庫考入到工程

在這里插入圖片描述

編寫CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)file(GLOB source_file *.cpp)message("source_file = ${source_file}")add_library(z-playerSHARED${source_file})include_directories(${CMAKE_SOURCE_DIR}/include)set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}")find_library(log-liblog)target_link_libraries(z-player# 方法一:使用-Wl 忽略順序
#        -Wl,--start-group #忽略順序引發(fā)的錯誤
#        avcodec avfilter avformat avutil swresample swscale
#        -Wl,--end-group# 方法二:調(diào)整順序avformat avcodec avfilter avutil swresample swscale #必須要把a(bǔ)vformat放在avcodec的前面${log-lib}z)

這里target_link_libraries方法有兩個(gè)問題:

1.FFmpeg是需要依賴了libz.so庫的如下圖:
在這里插入圖片描述
所有要在target_link_libraries方法里添加z,否則會報(bào)錯

在這里插入圖片描述
2.FFmpeg添加順序問題,
當(dāng)我們添加

avcodec avfilter avformat avutil swresample swscale

這樣一個(gè)順序時(shí)會報(bào)錯
在這里插入圖片描述

解決方法有兩個(gè):

第一個(gè):將avfilter放到avcodec前面就可以了

avformat avcodec avfilter avutil swresample swscale

第二個(gè):使用-Wl忽略順序

-Wl,--start-group #忽略順序引發(fā)的錯誤
avcodec avfilter avformat avutil swresample swscale
-Wl,--end-group

編碼

FFmpeg播放器結(jié)構(gòu)類圖

在這里插入圖片描述

編寫ZPlayer.java類
public class ZPlayer implements LifecycleObserver, SurfaceHolder.Callback {private static final String TAG = ZPlayer.class.getSimpleName();static {System.loadLibrary("z-player");}private final long nativeHandle;private OnPrepareListener listener;private OnErrorListener onErrorListener;private SurfaceHolder mHolder;private OnProgressListener onProgressListener;public ZPlayer() {nativeHandle = nativeInit();}/*** 設(shè)置播放顯示的畫布* @param surfaceView*/public void setSurfaceView(SurfaceView surfaceView) {if (this.mHolder != null) {mHolder.removeCallback(this); // 清除上一次的}mHolder = surfaceView.getHolder();mHolder.addCallback(this);}/*** 讓使用者設(shè)置播放的文件,或者直播地址* @param dataSource*/public void setDataSource(String dataSource){setDataSource(nativeHandle, dataSource);}/*** 準(zhǔn)備好要播放的視頻*/@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)public void prepare() {Log.e(TAG,"ZPlayer->prepare");nativePrepare(nativeHandle);}/*** 開始播放*/public void start(){Log.e(TAG,"ZPlayer->start");nativeStart(nativeHandle);}/*** 停止播放*/@OnLifecycleEvent(Lifecycle.Event.ON_STOP)public void stop(){Log.e(TAG,"ZPlayer->stop");nativeStop(nativeHandle);}@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)public void release(){Log.e(TAG,"ZPlayer->release");mHolder.removeCallback(this);nativeRelease(nativeHandle);}/*** JavaCallHelper 會反射調(diào)用此方法* @param errorCode*/public void onError(int errorCode, String ffmpegError){Log.e(TAG,"Java接收到回調(diào)了->onError:"+errorCode);String title = "\nFFmpeg給出的錯誤如下:\n";String msg = null;switch (errorCode){case Constant.FFMPEG_CAN_NOT_OPEN_URL:msg = "打不開視頻"+title+ ffmpegError;break;case Constant.FFMPEG_CAN_NOT_FIND_STREAMS:msg = "找不到流媒體"+title+ ffmpegError;break;case Constant.FFMPEG_FIND_DECODER_FAIL:msg = "找不到解碼器"+title+ ffmpegError;break;case Constant.FFMPEG_ALLOC_CODEC_CONTEXT_FAIL:msg = "無法根據(jù)解碼器創(chuàng)建上下文"+title+ ffmpegError;break;case Constant.FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL:msg = "根據(jù)流信息 配置上下文參數(shù)失敗"+title+ ffmpegError;break;case Constant.FFMPEG_OPEN_DECODER_FAIL:msg = "打開解碼器失敗"+title+ ffmpegError;break;case Constant.FFMPEG_NOMEDIA:msg = "沒有音視頻"+title+ ffmpegError;break;}if(onErrorListener != null ){onErrorListener.onError(msg);}}/*** JavaCallHelper 會反射調(diào)用此方法*/public void onPrepare(){Log.e(TAG,"Java接收到回調(diào)了->onPrepare");if(listener != null){listener.onPrepare();}}public void onProgress(int progress){if(onProgressListener != null){onProgressListener.onProgress(progress);}}@Overridepublic void surfaceCreated(SurfaceHolder surfaceHolder) {Log.e(TAG,"ZPlayer->surfaceCreated");}@Overridepublic void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {Log.e(TAG,"ZPlayer->surfaceChanged");nativeSetSurface(nativeHandle,surfaceHolder.getSurface());}@Overridepublic void surfaceDestroyed(SurfaceHolder surfaceHolder) {Log.e(TAG,"ZPlayer->surfaceDestroyed");}public int getDuration() {return getNativeDuration(nativeHandle);}public void seek(int playProgress) {nativeSeek(playProgress,nativeHandle);}public interface OnPrepareListener{void onPrepare();}public void setOnPrepareListener(OnPrepareListener listener){this.listener = listener;}public interface OnProgressListener{void onProgress(int progress);}public void setOnProgressListener(OnProgressListener listener){this.onProgressListener = listener;}public interface OnErrorListener{void onError(String errorCode);}public void setOnErrorListener(OnErrorListener listener){this.onErrorListener = listener;}private native long nativeInit();private native void setDataSource(long nativeHandle, String path);private  native void nativePrepare(long nativeHandle);private native void nativeStart(long nativeHandle);private native void nativeStop(long nativeHandle);private  native void nativeRelease(long nativeHandle);private native void nativeSetSurface(long nativeHandle, Surface surface);private native int getNativeDuration(long nativeHandle);private native void nativeSeek(int playValue,long nativeHandle);
}
編寫MainActivity.java類
public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {private ActivityMainBinding binding;private int PERMISSION_REQUEST = 0x1001;private ZPlayer mPlayer;// 用戶是否拖拽里private boolean isTouch = false;// 獲取native層的總時(shí)長private int duration ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());checkPermission();binding.seekBar.setOnSeekBarChangeListener(this);mPlayer = new ZPlayer();getLifecycle().addObserver(mPlayer);mPlayer.setSurfaceView(binding.surfaceView);mPlayer.setDataSource("/sdcard/demo.mp4");
//        mPlayer.setDataSource("/sdcard/chengdu.mp4");mPlayer.setOnPrepareListener(new ZPlayer.OnPrepareListener() {@Overridepublic void onPrepare() {duration = mPlayer.getDuration();runOnUiThread(new Runnable() {@Overridepublic void run() {binding.seekBarTimeLayout.setVisibility(duration != 0 ? View.VISIBLE : View.GONE);if(duration != 0){binding.tvTime.setText("00:00/"+getMinutes(duration)+":"+getSeconds(duration));}binding.tvState.setTextColor(Color.GREEN);binding.tvState.setText("恭喜init初始化成功");}});mPlayer.start();}});mPlayer.setOnErrorListener(new ZPlayer.OnErrorListener() {@Overridepublic void onError(String errorCode) {runOnUiThread(new Runnable() {@Overridepublic void run() {binding.tvState.setTextColor(Color.RED);binding.tvState.setText(errorCode);}});}});mPlayer.setOnProgressListener(new ZPlayer.OnProgressListener() {@Overridepublic void onProgress(int progress) {if (!isTouch){runOnUiThread(new Runnable() {@Overridepublic void run() {// 非直播,是本地視頻文件if(duration != 0) {binding.tvTime.setText(getMinutes(progress) + ":" + getSeconds(progress)+ "/" + getMinutes(duration) + ":" + getSeconds(duration));binding.seekBar.setProgress(progress * 100 / duration);}}});}}});}private void checkPermission() {if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=PackageManager.PERMISSION_GRANTED){Log.e("zuo","無權(quán)限,去申請權(quán)限");ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST);}else {Log.e("zuo","有權(quán)限");}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if(requestCode == PERMISSION_REQUEST){Log.e("zuo","申請到權(quán)限"+grantResults.length);if (grantResults[0] == PackageManager.PERMISSION_GRANTED){Toast.makeText(this,"已申請權(quán)限",Toast.LENGTH_SHORT).show();}else {Toast.makeText(this,"申請權(quán)限失敗",Toast.LENGTH_SHORT).show();}}}private String getSeconds(int duration){int seconds = duration % 60;String str ;if(seconds <= 9){str = "0"+seconds;}else {str = "" + seconds;}return str;}private String getMinutes(int duration){int minutes = duration / 60;String str ;if(minutes <= 9){str = "0"+minutes;}else {str = "" + minutes;}return str;}/*** 當(dāng)前拖動條進(jìn)度發(fā)送了改變,毀掉此方法* @param seekBar 控件* @param progress 1~100* @param fromUser 是否用戶拖拽導(dǎo)致到改變*/@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if(fromUser) {binding.tvTime.setText(getMinutes(progress * duration / 100) + ":" + getSeconds(progress * duration / 100)+ "/" + getMinutes(duration) + ":" + getSeconds(duration));}}//手按下去,毀掉此方法@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {isTouch = true;}// 手松開(SeekBar當(dāng)前值)回調(diào)此方法@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {isTouch = false;int seekBarProgress = seekBar.getProgress();int playProgress = seekBarProgress * duration / 100;mPlayer.seek(playProgress);}
}
編寫native-lib.cpp

java調(diào)用native方法的入口

#include <jni.h>
#include <string>
#include "ZPlayer.h"
#define LOG_TAG "native-lib"ZPlayer *zPlayer = nullptr;
JavaVM *javaVm = nullptr;
JavaCallHelper *helper = nullptr;
ANativeWindow *window = nullptr;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;int JNI_OnLoad(JavaVM *vm,void *r){javaVm = vm;return JNI_VERSION_1_6;
}extern "C"
JNIEXPORT jlong JNICALL
Java_com_zxj_zplayer_ZPlayer_nativeInit(JNIEnv *env, jobject thiz) {//創(chuàng)建播放器helper = new JavaCallHelper(javaVm,env,thiz);zPlayer = new ZPlayer(helper);return (jlong)zPlayer;
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_setDataSource(JNIEnv *env, jobject thiz, jlong native_handle,jstring path) {const char *dataSource = env->GetStringUTFChars(path, nullptr);ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);zPlayer->setDataSource(dataSource);env->ReleaseStringUTFChars(path,dataSource);
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_nativePrepare(JNIEnv *env, jobject thiz, jlong native_handle) {ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);zPlayer->prepare();
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_nativeStart(JNIEnv *env, jobject thiz, jlong native_handle) {ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);zPlayer->start();
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_nativeStop(JNIEnv *env, jobject thiz, jlong native_handle) {ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);zPlayer->stop();if(helper){DELETE(helper);}
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_nativeRelease(JNIEnv *env, jobject thiz, jlong native_handle) {pthread_mutex_lock(&mutex);if(window){ANativeWindow_release(window);window = nullptr;}pthread_mutex_unlock(&mutex);DELETE(helper);DELETE(zPlayer);DELETE(javaVm);DELETE(window);
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_nativeSetSurface(JNIEnv *env, jobject thiz, jlong native_handle,jobject surface) {pthread_mutex_lock(&mutex);//先釋放之前的顯示窗口if(window){LOGE("nativeSetSurface->window=%p",window);ANativeWindow_release(window);window = nullptr;}window = ANativeWindow_fromSurface(env,surface);ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);zPlayer->setWindow(window);pthread_mutex_unlock(&mutex);
}extern "C"
JNIEXPORT jint JNICALL
Java_com_zxj_zplayer_ZPlayer_getNativeDuration(JNIEnv *env, jobject thiz, jlong native_handle) {ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);if(zPlayer){return zPlayer->getDuration();}return 0;
}extern "C"
JNIEXPORT void JNICALL
Java_com_zxj_zplayer_ZPlayer_nativeSeek(JNIEnv *env, jobject thiz, jint play_value,jlong native_handle) {ZPlayer *zPlayer = reinterpret_cast<ZPlayer *>(native_handle);if(zPlayer){zPlayer->seek(play_value);}
}
編寫JavaCallHelper.cpp

這個(gè)類主要用作:處理音視頻后各個(gè)狀態(tài)回調(diào)java方法

#include "JavaCallHelper.h"
#define LOG_TAG "JavaCallHelper"JavaCallHelper::JavaCallHelper(JavaVM *vm, JNIEnv *env, jobject instace) {this->vm = vm;//如果在主線程回調(diào)this->env = env;//一旦涉及到j(luò)object 跨方法/跨線程 就需要創(chuàng)建全局引用this->instace = env->NewGlobalRef(instace);jclass clazz = env->GetObjectClass(instace);onErrorId = env->GetMethodID(clazz, "onError", "(ILjava/lang/String;)V");onPrepareId = env->GetMethodID(clazz, "onPrepare", "()V");onProgressId = env->GetMethodID(clazz, "onProgress", "(I)V");
}JavaCallHelper::~JavaCallHelper() {env->DeleteGlobalRef(this->instace);
}void JavaCallHelper::onError(int thread, int errorCode,char * ffmpegError) {//主線程if(thread == THREAD_MAIN){jstring _ffmpegError = env->NewStringUTF(ffmpegError);env->CallVoidMethod(instace,onErrorId,errorCode,_ffmpegError);} else{//子線程JNIEnv *env;if (vm->AttachCurrentThread(&env, 0) != JNI_OK) {return;}jstring _ffmpegError = env->NewStringUTF(ffmpegError);env->CallVoidMethod(instace,onErrorId,errorCode,_ffmpegError);vm->DetachCurrentThread();//解除附加,必須要}
}void JavaCallHelper::onPrepare(int thread) {//主線程if(thread == THREAD_MAIN){env->CallVoidMethod(instace,onPrepareId);} else{//子線程JNIEnv *env;if (vm->AttachCurrentThread(&env, 0) != JNI_OK) {return;}env->CallVoidMethod(instace,onPrepareId);vm->DetachCurrentThread();}
}void JavaCallHelper::onProgress(int thread, int progress) {if(thread == THREAD_MAIN){env->CallVoidMethod(instace,onProgressId,progress);} else{//子線程JNIEnv *env;if (vm->AttachCurrentThread(&env, 0) != JNI_OK) {return;}env->CallVoidMethod(instace,onProgressId,progress);vm->DetachCurrentThread();}
}
編寫ZPlayer.cpp

主要處理音視頻的

#include <cstring>
#include "ZPlayer.h"
#include "macro.h"void *task_prepare(void *args) {ZPlayer *zFmpeg = static_cast<ZPlayer *>(args);zFmpeg->_prepare();return 0;
}ZPlayer::ZPlayer(JavaCallHelper *callHelper, const char *dataSource) {this->callHelper = callHelper;//這樣寫會報(bào)錯,dataSource會在native-lib.cpp里的方法里會被釋放掉,那么這里拿到的dataSource是懸空指針
//    this->dataSource = const_cast<char *>(dataSource);this->dataSource = new char[strlen(dataSource)];strcpy(this->dataSource, dataSource);
}ZPlayer::~ZPlayer() {//釋放
//    delete this->dataSource;
//    this->dataSource = nullptr;DELETE(dataSource);DELETE(callHelper);
}void ZPlayer::prepare() {pthread_create(&pid, 0, task_prepare, this);
}void ZPlayer::_prepare() {//初始化網(wǎng)絡(luò),不調(diào)用這個(gè),FFmpage是無法聯(lián)網(wǎng)的avformat_network_init();//AVFormatContext 包含了視頻的信息(寬、高等)formatContext = 0;//1、打開媒體地址(文件地址、直播地址)int ret = avformat_open_input(&formatContext, dataSource, 0, 0);//ret不為0表示打開媒體失敗if (ret) {LOGE("打開媒體失敗:%s", av_err2str(ret));callHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_OPEN_URL);return;}//2、查找媒體中的音視頻流ret = avformat_find_stream_info(formatContext, 0);//小于0則失敗if (ret < 0) {LOGE("查找流失敗:%s", av_err2str(ret));callHelper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);return;}//經(jīng)過avformat_find_stream_info方法后,formatContext->nb_streams就有值了unsigned int streams = formatContext->nb_streams;//nb_streams :幾個(gè)流(幾段視頻/音頻)for (int i = 0; i < streams; ++i) {//可能代表是一個(gè)視頻,也可能代表是一個(gè)音頻AVStream *stream = formatContext->streams[i];//包含了解碼 這段流的各種參數(shù)信息(寬、高、碼率、幀率)AVCodecParameters *codecpar = stream->codecpar;//無論視頻還是音頻都需要干的一些事情(獲得解碼器)// 1、通過當(dāng)前流使用的編碼方式,查找解碼器AVCodec *avCodec = avcodec_find_decoder(codecpar->codec_id);if (avCodec == nullptr) {LOGE("查找解碼器失敗:%s", av_err2str(ret));callHelper->onError(THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);return;}//2、獲得解碼器上下文AVCodecContext *context3 = avcodec_alloc_context3(avCodec);if (context3 == nullptr) {LOGE("創(chuàng)建解碼上下文失敗:%s", av_err2str(ret));callHelper->onError(THREAD_CHILD, FFMPEG_ALLOC_CODEC_CONTEXT_FAIL);return;}//3、設(shè)置上下文內(nèi)的一些參數(shù) (context->width)ret = avcodec_parameters_to_context(context3, codecpar);if (ret < 0) {LOGE("設(shè)置解碼上下文參數(shù)失敗:%s", av_err2str(ret));callHelper->onError(THREAD_CHILD, FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL);return;}// 4、打開解碼器ret = avcodec_open2(context3, avCodec, 0);if (ret != 0) {LOGE("打開解碼器失敗:%s", av_err2str(ret));callHelper->onError(THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);return;}//音頻if (codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {audioChannel = new AudioChannel;} else if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {videoChannel = new VideoChannel;}}if (!audioChannel && !videoChannel) {LOGE("沒有音視頻");callHelper->onError(THREAD_CHILD, FFMPEG_NOMEDIA);return;}// 準(zhǔn)備完了 通知java 你隨時(shí)可以開始播放callHelper->onPrepare(THREAD_CHILD);
}

核心代碼差不多是這些,現(xiàn)在我們可以先測試一下,編譯運(yùn)行會發(fā)現(xiàn)報(bào)錯

在這里插入圖片描述
報(bào)這個(gè)錯是因?yàn)?#xff0c;FFmpeg的版本問題,在上一篇《FFmpeg編譯》中我們在編譯FFmpeg的庫的時(shí)候,指定了-D__ANDROID_API__=21,而我們項(xiàng)目中的minSdkVersion為14,所以需要修改minSdkVersion為21就可以了。

最后運(yùn)行測試是沒有問題的。

其他手機(jī)奔潰解決方法

上面編譯源碼使用到是"armeabi-v7a",但是有的手機(jī)是"arm64-v8a"架構(gòu)到,所以直接運(yùn)行就會報(bào)錯,找不到so庫

在這里插入圖片描述
1.這時(shí)有兩種解決方法

在build.gradle文件里加入ndk{abiFilters "armeabi-v7a"}就可以了
在這里插入圖片描述
2.重新編譯一個(gè)"arm64-v8a"的靜態(tài)庫,修改build.sh文件

#!/bin/bash#執(zhí)行生成makefile的shell腳本
PREFIX=./android/armeabi-v7a2NDK_ROOT=/home/zuojie/android-ndk-r17cCPU=aarch64-linux-android
TOOLCHAIN=$NDK_ROOT/toolchains/$CPU-4.9/prebuilt/linux-x86_64FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/$CPU -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"
#FLAGS="-isystem $NDK_ROOT/sysroot/usr/include/$CPU -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"INCLUDES=" -isystem $NDK_ROOT/sources/android/support/include"# \ 換行連接符
./configure --prefix=$PREFIX \--enable-small \--disable-programs \--disable-avdevice \--disable-postproc \--disable-encoders \--disable-muxers \--disable-filters \--enable-cross-compile \--cross-prefix=$TOOLCHAIN/bin/$CPU- \--disable-shared \--enable-static \--sysroot=$NDK_ROOT/platforms/android-21/arch-arm64 \--extra-cflags="$FLAGS $INCLUDES" \--extra-cflags="-isysroot $NDK_ROOT/sysroot/" \--arch=arm64 \--target-os=android # 清理一下 
make clean
#執(zhí)行makefile
make install

將編譯好生成的靜態(tài)庫考入到項(xiàng)目到指定目錄下
在這里插入圖片描述

原文地址: https://www.cnblogs.com/zuojie/p/16461050.html#autoid-1-2-0

主流的音視頻全棧開發(fā)技術(shù) 跳轉(zhuǎn)

整理了一些音視頻開發(fā)學(xué)習(xí)書籍、視頻資料(音視頻流媒體高級開發(fā)FFmpeg6.0/WebRTC/RTMP/RTSP/編碼解碼),有需要的可以自行添加學(xué)習(xí)交流群:739729163 領(lǐng)取!
在這里插入圖片描述

http://www.risenshineclean.com/news/8393.html

相關(guān)文章:

  • 做網(wǎng)站專家一個(gè)網(wǎng)站推廣
  • 彩票網(wǎng)站開發(fā)風(fēng)險(xiǎn)中美關(guān)系最新消息
  • wordpress彈移動端seo關(guān)鍵詞優(yōu)化
  • app軟件系統(tǒng)定制開發(fā)網(wǎng)站內(nèi)部鏈接優(yōu)化方法
  • 南京核酸最新通知重慶網(wǎng)站seo公司
  • 做張家界旅游網(wǎng)站多少錢全網(wǎng)營銷有哪些平臺
  • 深圳app開發(fā)公司大概價(jià)格seo關(guān)鍵詞分析表
  • 游戲源代碼網(wǎng)站百度推廣電話號碼
  • 手機(jī)網(wǎng)站開發(fā)公司百度搜索引擎api
  • 自助網(wǎng)站模板平臺北大青鳥
  • asp網(wǎng)站banner修改市場營銷計(jì)劃方案
  • 建設(shè) 大型電子商務(wù)網(wǎng)站杭州seo靠譜
  • 商城網(wǎng)站建設(shè)外貿(mào)平臺排名
  • 湛江市政工程建設(shè)公司網(wǎng)站美食軟文300范例
  • b2c電子商務(wù)網(wǎng)站的特點(diǎn)及類型2020年關(guān)鍵詞排名
  • 阿里云做網(wǎng)站可以嗎寧波seo資源
  • 做網(wǎng)站的榮譽(yù)證書網(wǎng)絡(luò)營銷這個(gè)專業(yè)怎么樣
  • 幼兒園網(wǎng)站建設(shè)情況統(tǒng)計(jì)表西安優(yōu)化seo托管
  • 宿遷哪里做網(wǎng)站內(nèi)蒙古網(wǎng)站seo
  • 最權(quán)威的做網(wǎng)站設(shè)計(jì)公司價(jià)格搜索引擎營銷ppt
  • 鄭州網(wǎng)站seo分析湖南seo推廣系統(tǒng)
  • dede 汽車網(wǎng)站谷歌競價(jià)排名推廣公司
  • 東湖網(wǎng)站建設(shè)啥是網(wǎng)絡(luò)推廣
  • WordPress前端上傳大文件搜索引擎優(yōu)化seo名詞解釋
  • 做設(shè)計(jì)網(wǎng)站的工作百度快照優(yōu)化培訓(xùn)班
  • 手機(jī)在線做ppt的網(wǎng)站有哪些最新的網(wǎng)絡(luò)營銷的案例
  • 做企業(yè)網(wǎng)站選百度云還是阿里云b2b網(wǎng)站推廣優(yōu)化
  • 成都營銷類網(wǎng)站設(shè)計(jì)如何優(yōu)化關(guān)鍵詞排名快速首頁
  • 免費(fèi)自己生成網(wǎng)站泰安網(wǎng)絡(luò)推廣培訓(xùn)
  • 網(wǎng)站網(wǎng)頁設(shè)計(jì)模板下載視頻seo優(yōu)化教程