Android 音视频ffmpeg
Posted 那年我七岁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 音视频ffmpeg 相关的知识,希望对你有一定的参考价值。
整个音视频流程
第一节,先做准备工作,功能有:1.前端展示; 2.解封装;3.各种错误回调
为什么要解封装,因为解完封装我们才能拿倒音视频信息,音视频流;
拿到音视频流我们才分开去解数据,才能绘制视频;
解封装的步骤及用的的函数:
第一步:打开媒体地址(文件路径, 直播地址rtmp)
avformat_alloc_context
avformat_open_input
第二步:查找媒体中的音视频流的信息
avformat_find_stream_info
第三步:根据流信息,流的个数,用循环来找
第四步:获取媒体流(视频,音频)
formatContext->streams[stream_index];
第五步:从上面的流中 获取 编码解码的【参数】
AVCodecParameters *parameters = stream->codecpar;
第六步:(根据上面的【参数】)获取编解码器
avcodec_find_decoder(parameters->codec_id);
第七步:编解码器 上下文
avcodec_alloc_context3(codec);
第八步:他目前是一张白纸(parameters copy codecContext)
avcodec_parameters_to_context(codecContext, parameters);
第九步:打开解码器
avcodec_open2(codecContext, codec, nullptr)
第十步:从编解码器参数中,获取流的类型 codec_type === 音频 视频
parameters->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO
第十一步: 如果流中 没有音频 也没有 视频 【健壮性校验】
第十二步:恭喜你,通知给上层;准备播放
native-lib.cpp 代码:
JavaVM *vm = 0;
ANativeWindow *window = 0;
Player *player = nullptr;
jint JNI_OnLoad(JavaVM * vm, void * args) {
::vm = vm;
return JNI_VERSION_1_6;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_game_demondk_DerryPlayer_prepareNative(JNIEnv *env, jobject job, jstring data_source) {
const char * source = env->GetStringUTFChars(data_source,0);
auto *hepler = new JINCallbackHelper(vm,env,job);
player = new Player(env, source, hepler);
player->perpare();
}
Player.cpp代码:
Player::Player(JNIEnv *env, const char *data_source, JINCallbackHelper *pHelper) {
this->env = env;
this->data_source = new char[strlen(data_source) + 1];
this->helper = pHelper;
strcpy(this->data_source, data_source);
}
void *task_perpare(void *args) {
auto *player = static_cast<Player *>(args);
player->perpare_();
return nullptr;
}
void Player::perpare() {
pthread_create(thread_t, 0, task_perpare, this);
}
void Player::perpare_() {
/**
* TODO 第一步:打开媒体地址(文件路径, 直播地址rtmp)
*/
avFormatContext = avformat_alloc_context();
if (!avFormatContext) {
return;
}
//添加参数
AVDictionary *dictionary = nullptr;
av_dict_set(&dictionary, "timeout", "5000000", 0);
/**
* 1,AVFormatContext *
* 2,路径 url:文件路径或直播地址
* 3,AVInputFormat *fmt Mac、Windows 摄像头、麦克风, 我们目前安卓用不到
* 4,各种设置:例如:Http 连接超时, 打开rtmp的超时 AVDictionary **options
*/
int result = avformat_open_input(&avFormatContext, this->data_source, nullptr, &dictionary);
// 释放字典
av_dict_free(&dictionary);
if (result) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_OPEN_URL);
}
//关闭打开流
avformat_close_input(&avFormatContext);
return;
}
/**
* TODO 第二步:查找媒体中的音视频流的信息
*/
result = avformat_find_stream_info(avFormatContext, nullptr);
if (result < 0) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_CAN_NOT_FIND_STREAMS);
}
avformat_close_input(&avFormatContext);
return;
}
//得到的总时长,要通过转换
this->duration = avFormatContext->duration / AV_TIME_BASE;
AVCodecContext *avCodecContext = nullptr;
for (int i = 0; i < avFormatContext->nb_streams; ++i) {
/**
* TODO 第四步:获取媒体流(视频,音频)
*/
AVStream *avStream = avFormatContext->streams[i];
/**
* TODO 第五步:从上面的流中 获取 编码解码的【参数】
* 由于:后面的编码器 解码器 都需要参数(宽高 等等)
*/
AVCodecParameters *parameters = avStream->codecpar;
/**
* TODO 第六步:(根据上面的【参数】)获取编解码器
*/
AVCodec *codec = avcodec_find_decoder(parameters->codec_id);
if (!codec) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_FIND_DECODER_FAIL);
}
avformat_close_input(&avFormatContext);
}
/**
* TODO 第七步:编解码器 上下文 (这个才是真正干活的)
*/
avCodecContext = avcodec_alloc_context3(codec);
if (!avCodecContext) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_ALLOC_CODEC_CONTEXT_FAIL);
}
avcodec_free_context(&avCodecContext);
avformat_close_input(&avFormatContext);
}
/**
* TODO 第八步:他目前是一张白纸(parameters copy codecContext)
*/
result = avcodec_parameters_to_context(avCodecContext, parameters);
if (result < 0) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_CODEC_CONTEXT_PARAMETERS_FAIL);
}
avcodec_free_context(&avCodecContext);
avformat_close_input(&avFormatContext);
return;
}
/**
* TODO 第九步:打开解码器
*/
result = avcodec_open2(avCodecContext, codec, nullptr);
if (result) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_OPEN_DECODER_FAIL);
}
avcodec_free_context(&avCodecContext);
avformat_close_input(&avFormatContext);
return;
}
//音视频同步需要参数
AVRational time_base = avStream->time_base;
/**
* TODO 第十步:从编解码器参数中,获取流的类型 codec_type === 音频 视频
*/
if (parameters->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
} else if (parameters->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
}
}
/**
* TODO 第十一步: 如果流中 没有音频 也没有 视频 【健壮性校验】
*/
if (!audioChannel && !videoChannel) {
if (helper) {
helper->onError(THREAD_CHILD, FFMPEG_NOMEDIA);
}
if (avCodecContext) {
//释放avCodecContext
avcodec_free_context(&avCodecContext);
}
avformat_close_input(&avFormatContext);
}
/**
* TODO 第十二步:准备成功,通知给上层
*/
if (helper) { // 只要用户关闭了,就不准你回调给Java成 start播放
helper->onPrepared(THREAD_CHILD);
}
}
static { System.loadLibrary("native-lib"); }
加载native-lib的时候就会执行下面函数,javaVM 主要用来处理子线程回调,因为子线程env不可以垮线程执行,所以需要javaVM里的线程处理
jint JNI_OnLoad(JavaVM * vm, void * args) { ::vm = vm; return JNI_VERSION_1_6; }
处理回调方法:
vm->AttachCurrentThread(&env_child, 0);
env_child->CallVoidMethod(job, jmd_prepared);
vm->DetachCurrentThread();
以上是关于Android 音视频ffmpeg 的主要内容,如果未能解决你的问题,请参考以下文章