系列文章:
音视频编解码全流程之用Extractor后Muxer生成MP4
根据前面叙述的 音视频编解码全流程之Extractor 可知:
媒体文件提取数据包的初始化及提取流程;
依此为基础现在本篇文章中实现“从媒体文件中Extractor提取数据包后,对提取后的数据进行解码的操作”。
一 .FFmpeg中提取媒体文件数据包后,对进行数据包解码:
1.FFmpeg交叉编译:
首先的是FFmpeg交叉编译,在之前的博客中有介绍交叉编译的全过程,感兴趣的可以查看博客:
2.提取媒体文件数据包:
具体的流程细节分解在 音视频编解码全流程之复用器Muxer 已经进行了描述,这里不再赘述。
3.查找解码器AVCodec和分配解码器实例AVCodecContext:
——> 这里是从媒体文件中封装实例AVFormatContext,查找音视频文件中的流信息 avformat_find_stream_info(in_fmt_ctx, nullptr)
——> 从封装实例中查找视频流的索引video_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
——> 查找视频流AVStream,src_video = in_fmt_ctx->streams[video_index]。
——> 根据编解码器ID,查找解码器AVCodec *video_codec = (AVCodec *) avcodec_find_decoder(video_codec_id);
以下是查找解码器AVCodec的函数代码:
1// 打开输入文件 2int RecodecVideo::open_input_file(const char *src_name) { 3 // 打开音视频文件 4 int ret = avformat_open_input(&in_fmt_ctx, src_name, nullptr, nullptr); 5 if (ret < 0) { 6 LOGE("Can't open file %s.\n", src_name); 7 recodecInfo = "\n Can't open file :" + string(src_name); 8 PostRecodecStatusMessage(recodecInfo.c_str()); 9 return -1; 10 } 11 LOGI("Success open input_file %s.\n", src_name); 12 recodecInfo = "\n Success open input_file:" + string(src_name); 13 PostRecodecStatusMessage(recodecInfo.c_str()); 14 // 查找音视频文件中的流信息 15 ret = avformat_find_stream_info(in_fmt_ctx, nullptr); 16 if (ret < 0) { 17 LOGE("Can't find stream information.\n"); 18 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 19 recodecInfo = "Can't find stream information:" + to_string(ret) + "\n error msg:" + 20 string(errbuf) + "\n"; 21 PostRecodecStatusMessage(recodecInfo.c_str()); 22 return -1; 23 } 24 // 找到视频流的索引 25 video_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); 26 if (video_index >= 0) { 27 src_video = in_fmt_ctx->streams[video_index]; 28 enum AVCodecID video_codec_id = src_video->codecpar->codec_id; 29 // 查找视频解码器 30 AVCodec *video_codec = (AVCodec *) avcodec_find_decoder(video_codec_id); 31 if (!video_codec) { 32 LOGE("video_codec not found\n"); 33 recodecInfo = "\n video_codec not found. "; 34 PostRecodecStatusMessage(recodecInfo.c_str()); 35 return -1; 36 } 37 video_decode_ctx = avcodec_alloc_context3(video_codec); // 分配解码器的实例 38 if (!video_decode_ctx) { 39 LOGE("video_decode_ctx is nullptr\n"); 40 recodecInfo = "\n video_decode_ctx is nullptr "; 41 PostRecodecStatusMessage(recodecInfo.c_str()); 42 return -1; 43 } 44 // 把视频流中的编解码参数复制给解码器的实例 45 avcodec_parameters_to_context(video_decode_ctx, src_video->codecpar); 46 ret = avcodec_open2(video_decode_ctx, video_codec, nullptr); // 打开解码器的实例 47 if (ret < 0) { 48 LOGE("Can't open video_decode_ctx.\n"); 49 recodecInfo = "Can't open video_decode_ctx\n"; 50 PostRecodecStatusMessage(recodecInfo.c_str()); 51 return -1; 52 } 53 } else { 54 LOGE("Can't find video stream.\n"); 55 recodecInfo = "\n Can't find video stream."; 56 PostRecodecStatusMessage(recodecInfo.c_str()); 57 return -1; 58 } 59 // 找到音频流的索引 60 audio_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); 61 if (audio_index >= 0) { 62 src_audio = in_fmt_ctx->streams[audio_index]; 63 } 64 return 0; 65}
4.对数据包进行解码:
由上的提取过程后从文件中提取出AVPacket,在FFmpeg中接收AVFrame后对其解码出原始的数据帧AVFrame。
——> 把未解压的数据包发给解码器实例avcodec_send_packet(video_decode_ctx, packet);
——> 从解码器实例获取还原后的数据帧avcodec_receive_frame(video_decode_ctx, frame);
以下是把AVPacket发送给解码器解码后得到AVFrame的函数代码:
1 2// 对视频帧重新编码 3int RecodecVideo::recode_video(AVPacket *packet, AVFrame *frame) { 4 // 把未解压的数据包发给解码器实例 5 int ret = avcodec_send_packet(video_decode_ctx, packet); 6 if (ret < 0) { 7 LOGE("send packet occur error %d.\n", ret); 8 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 9 recodecInfo = "send packet occur error:" + to_string(ret) + "\n error msg:" + 10 string(errbuf) + "\n"; 11 PostRecodecStatusMessage(recodecInfo.c_str()); 12 return ret; 13 } 14 while (1) { 15 // 从解码器实例获取还原后的数据帧 16 ret = avcodec_receive_frame(video_decode_ctx, frame); 17 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { 18 return (ret == AVERROR(EAGAIN)) ? 0 : 1; 19 } else if (ret < 0) { 20 LOGE("decode frame occur error %d.\n", ret); 21 recodecInfo = "\n decode frame occur error :" + to_string(ret); 22 PostRecodecStatusMessage(recodecInfo.c_str()); 23 break; 24 } 25 if (frame->pts == AV_NOPTS_VALUE) { // 对H.264裸流做特殊处理 26 double interval = 1.0 / av_q2d(src_video->r_frame_rate); 27 frame->pts = count * interval / av_q2d(src_video->time_base); 28 count++; 29 } 30 output_video(frame); // 给视频帧编码,并写入压缩后的视频包 31 } 32 return ret; 33}
5.完整的编解码的代码:
以上的代码放在本人的GitHub项目中:https://github.com/wangyongyao1989/FFmpegPractices
中的RecodecVideo.cpp:
1// 2// Created by wangyao on 2025/8/17. 3// 4 5#include "includes/RecodecVideo.h" 6 7RecodecVideo::RecodecVideo(JNIEnv *env, jobject thiz) { 8 mEnv = env; 9 env->GetJavaVM(&mJavaVm); 10 mJavaObj = env->NewGlobalRef(thiz); 11} 12 13RecodecVideo::~RecodecVideo() { 14 if (in_fmt_ctx) { 15 in_fmt_ctx = nullptr; 16 } 17 if (video_encode_ctx) { 18 video_encode_ctx = nullptr; 19 } 20 video_index = -1; 21 audio_index = -1; 22 if (src_video) { 23 src_video = nullptr; 24 } 25 if (src_audio) { 26 src_audio = nullptr; 27 } 28 if (dest_video) { 29 dest_video = nullptr; 30 } 31 if (out_fmt_ctx) { 32 out_fmt_ctx = nullptr; 33 } 34 if (video_encode_ctx) { 35 video_encode_ctx = nullptr; 36 } 37 mEnv->DeleteGlobalRef(mJavaObj); 38 if (mEnv) { 39 mEnv = nullptr; 40 } 41 42 if (mJavaVm) { 43 mJavaVm = nullptr; 44 } 45 46 if (mJavaObj) { 47 mJavaObj = nullptr; 48 } 49 50 if (codecThread != nullptr) { 51 codecThread->join(); 52 delete codecThread; 53 codecThread = nullptr; 54 } 55 56 mSrcPath = nullptr; 57 mDestPath = nullptr; 58 59} 60 61void RecodecVideo::startRecodecThread(const char *srcPath, const char *destPath) { 62 mSrcPath = srcPath; 63 mDestPath = destPath; 64 if (open_input_file(mSrcPath) < 0) { // 打开输入文件 65 return; 66 } 67 if (open_output_file(mDestPath) < 0) { // 打开输出文件 68 return; 69 } 70 if (codecThread == nullptr) { 71 codecThread = new thread(DoRecoding, this); 72 codecThread->detach(); 73 } 74} 75 76void RecodecVideo::DoRecoding(RecodecVideo *recodecVideo) { 77 recodecVideo->recodecVideo(); 78} 79 80void RecodecVideo::recodecVideo() { 81 int ret = -1; 82 AVPacket *packet = av_packet_alloc(); // 分配一个数据包 83 AVFrame *frame = av_frame_alloc(); // 分配一个数据帧 84 while (av_read_frame(in_fmt_ctx, packet) >= 0) { // 轮询数据包 85 if (packet->stream_index == video_index) { // 视频包需要重新编码 86 packet->stream_index = 0; 87 if (packet->buf->size < 600) { 88 recodecInfo = 89 "读出视频包的大小:" + to_string(packet->buf->size) + ",并重新编码写入...\n"; 90 PostRecodecStatusMessage(recodecInfo.c_str()); 91 } 92 LOGD("%s.\n", recodecInfo.c_str()); 93 recode_video(packet, frame); // 对视频帧重新编码 94 } else { // 音频包暂不重新编码,直接写入目标文件 95 packet->stream_index = 1; 96 ret = av_write_frame(out_fmt_ctx, packet); // 往文件写入一个数据包 97 if (ret < 0) { 98 LOGE("write frame occur error %d.\n", ret); 99 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 100 recodecInfo = "write frame occur error:" + to_string(ret) + "\n error msg:" + 101 string(errbuf) + "\n"; 102 recodecInfo = "\n write frame occur error:" + to_string(ret); 103 break; 104 } 105 } 106 av_packet_unref(packet); // 清除数据包 107 } 108 packet->data = nullptr; // 传入一个空包,冲走解码缓存 109 packet->size = 0; 110 recode_video(packet, frame); // 对视频帧重新编码 111 output_video(nullptr); // 传入一个空帧,冲走编码缓存 112 av_write_trailer(out_fmt_ctx); // 写文件尾 113 LOGI("Success recode file.\n"); 114 recodecInfo = "Success recode file!!!!!!\n\n"; 115 PostRecodecStatusMessage(recodecInfo.c_str()); 116 117 av_frame_free(&frame); // 释放数据帧资源 118 av_packet_free(&packet); // 释放数据包资源 119 avio_close(out_fmt_ctx->pb); // 关闭输出流 120 avcodec_close(video_decode_ctx); // 关闭视频解码器的实例 121 avcodec_free_context(&video_decode_ctx); // 释放视频解码器的实例 122 avcodec_close(video_encode_ctx); // 关闭视频编码器的实例 123 avcodec_free_context(&video_encode_ctx); // 释放视频编码器的实例 124 avformat_free_context(out_fmt_ctx); // 释放封装器的实例 125 avformat_close_input(&in_fmt_ctx); // 关闭音视频文件 126} 127 128// 打开输入文件 129int RecodecVideo::open_input_file(const char *src_name) { 130 // 打开音视频文件 131 int ret = avformat_open_input(&in_fmt_ctx, src_name, nullptr, nullptr); 132 if (ret < 0) { 133 LOGE("Can't open file %s.\n", src_name); 134 recodecInfo = "\n Can't open file :" + string(src_name); 135 PostRecodecStatusMessage(recodecInfo.c_str()); 136 return -1; 137 } 138 LOGI("Success open input_file %s.\n", src_name); 139 recodecInfo = "\n Success open input_file:" + string(src_name); 140 PostRecodecStatusMessage(recodecInfo.c_str()); 141 // 查找音视频文件中的流信息 142 ret = avformat_find_stream_info(in_fmt_ctx, nullptr); 143 if (ret < 0) { 144 LOGE("Can't find stream information.\n"); 145 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 146 recodecInfo = "Can't find stream information:" + to_string(ret) + "\n error msg:" + 147 string(errbuf) + "\n"; 148 PostRecodecStatusMessage(recodecInfo.c_str()); 149 return -1; 150 } 151 // 找到视频流的索引 152 video_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); 153 if (video_index >= 0) { 154 src_video = in_fmt_ctx->streams[video_index]; 155 enum AVCodecID video_codec_id = src_video->codecpar->codec_id; 156 // 查找视频解码器 157 AVCodec *video_codec = (AVCodec *) avcodec_find_decoder(video_codec_id); 158 if (!video_codec) { 159 LOGE("video_codec not found\n"); 160 recodecInfo = "\n video_codec not found. "; 161 PostRecodecStatusMessage(recodecInfo.c_str()); 162 return -1; 163 } 164 video_decode_ctx = avcodec_alloc_context3(video_codec); // 分配解码器的实例 165 if (!video_decode_ctx) { 166 LOGE("video_decode_ctx is nullptr\n"); 167 recodecInfo = "\n video_decode_ctx is nullptr "; 168 PostRecodecStatusMessage(recodecInfo.c_str()); 169 return -1; 170 } 171 // 把视频流中的编解码参数复制给解码器的实例 172 avcodec_parameters_to_context(video_decode_ctx, src_video->codecpar); 173 ret = avcodec_open2(video_decode_ctx, video_codec, nullptr); // 打开解码器的实例 174 if (ret < 0) { 175 LOGE("Can't open video_decode_ctx.\n"); 176 recodecInfo = "Can't open video_decode_ctx\n"; 177 PostRecodecStatusMessage(recodecInfo.c_str()); 178 return -1; 179 } 180 } else { 181 LOGE("Can't find video stream.\n"); 182 recodecInfo = "\n Can't find video stream."; 183 PostRecodecStatusMessage(recodecInfo.c_str()); 184 return -1; 185 } 186 // 找到音频流的索引 187 audio_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); 188 if (audio_index >= 0) { 189 src_audio = in_fmt_ctx->streams[audio_index]; 190 } 191 return 0; 192} 193 194// 给视频帧编码,并写入压缩后的视频包 195int RecodecVideo::output_video(AVFrame *frame) { 196 // 把原始的数据帧发给编码器实例 197 int ret = avcodec_send_frame(video_encode_ctx, frame); 198 if (ret < 0) { 199 LOGE("send frame occur error %d.\n", ret); 200 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 201 recodecInfo = "send frame occur error :" + to_string(ret) + "\n error msg:" + 202 string(errbuf) + "\n"; 203 PostRecodecStatusMessage(recodecInfo.c_str()); 204 return ret; 205 } 206 while (1) { 207 AVPacket *packet = av_packet_alloc(); // 分配一个数据包 208 // 从编码器实例获取压缩后的数据包 209 ret = avcodec_receive_packet(video_encode_ctx, packet); 210 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { 211 return (ret == AVERROR(EAGAIN)) ? 0 : 1; 212 } else if (ret < 0) { 213 LOGE("encode frame occur error %d.\n", ret); 214 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 215 recodecInfo = "encode frame occur error :" + to_string(ret) + "\n error msg:" + 216 string(errbuf) + "\n"; 217 PostRecodecStatusMessage(recodecInfo.c_str()); 218 break; 219 } 220 // 把数据包的时间戳从一个时间基转换为另一个时间基 221 av_packet_rescale_ts(packet, src_video->time_base, dest_video->time_base); 222// LOGI( "pts=%ld, dts=%ld.\n", packet->pts, packet->dts); 223 packet->stream_index = 0; 224 ret = av_write_frame(out_fmt_ctx, packet); // 往文件写入一个数据包 225 if (ret < 0) { 226 LOGE("write frame occur error %d.\n", ret); 227 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 228 recodecInfo = "write frame occur error:" + to_string(ret) + "\n error msg:" + 229 string(errbuf) + "\n"; 230 PostRecodecStatusMessage(recodecInfo.c_str()); 231 break; 232 } 233 av_packet_unref(packet); // 清除数据包 234 } 235 return ret; 236} 237 238// 对视频帧重新编码 239int RecodecVideo::recode_video(AVPacket *packet, AVFrame *frame) { 240 // 把未解压的数据包发给解码器实例 241 int ret = avcodec_send_packet(video_decode_ctx, packet); 242 if (ret < 0) { 243 LOGE("send packet occur error %d.\n", ret); 244 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 245 recodecInfo = "send packet occur error:" + to_string(ret) + "\n error msg:" + 246 string(errbuf) + "\n"; 247 PostRecodecStatusMessage(recodecInfo.c_str()); 248 return ret; 249 } 250 while (1) { 251 // 从解码器实例获取还原后的数据帧 252 ret = avcodec_receive_frame(video_decode_ctx, frame); 253 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { 254 return (ret == AVERROR(EAGAIN)) ? 0 : 1; 255 } else if (ret < 0) { 256 LOGE("decode frame occur error %d.\n", ret); 257 recodecInfo = "\n decode frame occur error :" + to_string(ret); 258 PostRecodecStatusMessage(recodecInfo.c_str()); 259 break; 260 } 261 if (frame->pts == AV_NOPTS_VALUE) { // 对H.264裸流做特殊处理 262 double interval = 1.0 / av_q2d(src_video->r_frame_rate); 263 frame->pts = count * interval / av_q2d(src_video->time_base); 264 count++; 265 } 266 output_video(frame); // 给视频帧编码,并写入压缩后的视频包 267 } 268 return ret; 269} 270 271int RecodecVideo::open_output_file(const char *dest_name) { 272 // 分配音视频文件的封装实例 273 int ret = avformat_alloc_output_context2(&out_fmt_ctx, nullptr, nullptr, dest_name); 274 if (ret < 0) { 275 LOGE("Can't alloc output_file %s.\n", dest_name); 276 recodecInfo = "\n Can't alloc output_file :" + string(dest_name); 277 PostRecodecStatusMessage(recodecInfo.c_str()); 278 return -1; 279 } 280 // 打开输出流 281 ret = avio_open(&out_fmt_ctx->pb, dest_name, AVIO_FLAG_READ_WRITE); 282 if (ret < 0) { 283 LOGE("Can't open output_file %s.\n", dest_name); 284 recodecInfo = "\n Can't open output_file:" + string(dest_name); 285 PostRecodecStatusMessage(recodecInfo.c_str()); 286 return -1; 287 } 288 LOGI("Success open output_file %s.\n", dest_name); 289 recodecInfo = "\n Success open output_file :" + string(dest_name); 290 PostRecodecStatusMessage(recodecInfo.c_str()); 291 if (video_index >= 0) { // 创建编码器实例和新的视频流 292 enum AVCodecID video_codec_id = src_video->codecpar->codec_id; 293 // 查找视频编码器 294// AVCodec *video_codec = (AVCodec *) avcodec_find_encoder(video_codec_id); 295 //使用libx264的编码器 296 AVCodec *video_codec = (AVCodec *) avcodec_find_encoder_by_name("libx264"); 297 if (!video_codec) { 298 LOGE("video_codec not found\n"); 299 recodecInfo = "\n video_codec not found ."; 300 PostRecodecStatusMessage(recodecInfo.c_str()); 301 return -1; 302 } 303 video_encode_ctx = avcodec_alloc_context3(video_codec); // 分配编码器的实例 304 if (!video_encode_ctx) { 305 LOGE("video_encode_ctx is null\n"); 306 recodecInfo = "\n video_encode_ctx is null"; 307 PostRecodecStatusMessage(recodecInfo.c_str()); 308 return -1; 309 } 310 // 把源视频流中的编解码参数复制给编码器的实例 311 avcodec_parameters_to_context(video_encode_ctx, src_video->codecpar); 312 // 注意:帧率和时间基要单独赋值,因为avcodec_parameters_to_context没复制这两个参数 313 video_encode_ctx->framerate = src_video->r_frame_rate; 314 // framerate.num值过大,会导致视频头一秒变灰色 315 if (video_encode_ctx->framerate.num > 60) { 316 video_encode_ctx->framerate = (AVRational) {25, 1}; // 帧率 317 } 318 video_encode_ctx->time_base = src_video->time_base; 319 video_encode_ctx->gop_size = 12; // 关键帧的间隔距离 320 //video_encode_ctx->max_b_frames = 0; // 0表示不要B帧 321 // AV_CODEC_FLAG_GLOBAL_HEADER标志允许操作系统显示该视频的缩略图 322 if (out_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) { 323 video_encode_ctx->flags = AV_CODEC_FLAG_GLOBAL_HEADER; 324 } 325 ret = avcodec_open2(video_encode_ctx, video_codec, nullptr); // 打开编码器的实例 326 if (ret < 0) { 327 LOGE("Can't open video_encode_ctx.\n"); 328 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 329 LOGE("avcodec_open2失败:%s\n", errbuf); 330 recodecInfo = "\n avcodec_open2失败:" + string(errbuf); 331 PostRecodecStatusMessage(recodecInfo.c_str()); 332 return -1; 333 } 334 dest_video = avformat_new_stream(out_fmt_ctx, nullptr); // 创建数据流 335 // 把编码器实例的参数复制给目标视频流 336 avcodec_parameters_from_context(dest_video->codecpar, video_encode_ctx); 337 // 如果后面有对视频帧转换时间基,这里就无需复制时间基 338 //dest_video->time_base = src_video->time_base; 339 dest_video->codecpar->codec_tag = 0; 340 } 341 if (audio_index >= 0) { // 源文件有音频流,就给目标文件创建音频流 342 AVStream *dest_audio = avformat_new_stream(out_fmt_ctx, nullptr); // 创建数据流 343 // 把源文件的音频参数原样复制过来 344 avcodec_parameters_copy(dest_audio->codecpar, src_audio->codecpar); 345 dest_audio->codecpar->codec_tag = 0; 346 } 347 ret = avformat_write_header(out_fmt_ctx, nullptr); // 写文件头 348 if (ret < 0) { 349 LOGE("write file_header occur error %d.\n", ret); 350 recodecInfo = "\n write file_header occur error :" + to_string(ret); 351 av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串 352 recodecInfo = "\n avformat_write_header 失败:" + string(errbuf); 353 PostRecodecStatusMessage(recodecInfo.c_str()); 354 return -1; 355 } 356 LOGI("Success write file_header.\n"); 357 recodecInfo = "Success write file_header.\n"; 358 PostRecodecStatusMessage(recodecInfo.c_str()); 359 return 0; 360} 361 362 363JNIEnv *RecodecVideo::GetJNIEnv(bool *isAttach) { 364 JNIEnv *env; 365 int status; 366 if (nullptr == mJavaVm) { 367 LOGD("RecodecVideo::GetJNIEnv mJavaVm == nullptr"); 368 return nullptr; 369 } 370 *isAttach = false; 371 status = mJavaVm->GetEnv((void **) &env, JNI_VERSION_1_6); 372 if (status != JNI_OK) { 373 status = mJavaVm->AttachCurrentThread(&env, nullptr); 374 if (status != JNI_OK) { 375 LOGD("RecodecVideo::GetJNIEnv failed to attach current thread"); 376 return nullptr; 377 } 378 *isAttach = true; 379 } 380 return env; 381} 382 383void RecodecVideo::PostRecodecStatusMessage(const char *msg) { 384 bool isAttach = false; 385 JNIEnv *pEnv = GetJNIEnv(&isAttach); 386 if (pEnv == nullptr) { 387 return; 388 } 389 jobject javaObj = mJavaObj; 390 jmethodID mid = pEnv->GetMethodID(pEnv->GetObjectClass(javaObj), "CppStatusCallback", 391 "(Ljava/lang/String;)V"); 392 jstring pJstring = pEnv->NewStringUTF(msg); 393 pEnv->CallVoidMethod(javaObj, mid, pJstring); 394 if (isAttach) { 395 JavaVM *pJavaVm = mJavaVm; 396 pJavaVm->DetachCurrentThread(); 397 } 398} 399 400
二.MediaCodec中提取媒体文件数据包后,对进行数据包解码:
硬件编解码在Android中必须用到MediaCodec提供的下层编解码芯片的接口,在NdkMediaCodec中AMediaCodec_createDecoderByType方法来获取解码器。
1.初始化Extractor:
——> 创建 AMediaExtractor_new()
——> 设置Extractor的fd AMediaExtractor_setDataSourceFd(extractor, input_fd, 0, fileSize)
1bool MediaExtratorDecodec::initExtractor() { 2 3 extractor = AMediaExtractor_new(); 4 if (!extractor) { 5 LOGE("Failed to create media extractor "); 6 callbackInfo = 7 "Failed to create media extractor \n"; 8 PostStatusMessage(callbackInfo.c_str()); 9 return false; 10 } 11 LOGE("inputPath:%s", sSrcPath.c_str()); 12 FILE *inputFp = fopen(sSrcPath.c_str(), "rb"); 13 if (!inputFp) { 14 LOGE("Unable to open output file :%s", sSrcPath.c_str()); 15 callbackInfo = 16 "Unable to open output file :" + sSrcPath + "\n"; 17 PostStatusMessage(callbackInfo.c_str()); 18 return false; 19 } 20 struct stat buf; 21 stat(sSrcPath.c_str(), &buf); 22 size_t fileSize = buf.st_size; 23 int32_t input_fd = fileno(inputFp); 24 25 LOGE("input_fd:%d", input_fd); 26 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, input_fd, 0, fileSize); 27 if (status != AMEDIA_OK) { 28 LOGE("Failed to set data source: %d", status); 29 callbackInfo = 30 "Failed to set data source :" + to_string(status) + "\n"; 31 PostStatusMessage(callbackInfo.c_str()); 32 return false; 33 } 34 35 LOGI("Extractor initialized successfully"); 36 return true; 37}
2.选择轨道获取AMediaFormat:
——> AMediaExtractor_getTrackCount()获取取器Extractor中的轨道数
——> AMediaExtractor_getTrackFormat(extractor, i) 遍历轨道从中获取每个轨道中的AMediaFormat
——> 筛选出音频轨道和视频轨道,分别获取音频轨道的audioTrackIndex值和视频轨道videoTrackIndex值。
1// 选择轨道获取AMediaFormat 2bool MediaExtratorDecodec::selectTracksAndGetFormat() { 3 LOGI("selectTracksAndGetFormat==========="); 4 5 size_t trackCount = AMediaExtractor_getTrackCount(extractor); 6 LOGI("Total tracks: %zu", trackCount); 7 callbackInfo = 8 "Total tracks:" + to_string(trackCount) + "\n"; 9 PostStatusMessage(callbackInfo.c_str()); 10 11 for (size_t i = 0; i < trackCount; i++) { 12 AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, i); 13 if (!format) continue; 14 15 const char *mime; 16 if (AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) { 17 LOGI("Track %zu: MIME=%s", i, mime); 18 19 if (strncmp(mime, "video/", 6) == 0 && videoTrackIndex == -1) { 20 videoTrackIndex = i; 21 hasVideo = true; 22 // 获取视频格式信息 23 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &videoWidth); 24 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &videoHeight); 25 AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &videoDuration); 26 LOGI("Selected video track: %d", videoTrackIndex); 27 28 LOGI("Video track: %dx%d, duration: %lld us", 29 videoWidth, videoHeight, videoDuration); 30 callbackInfo = 31 "Selected video track:" + to_string(videoTrackIndex) + "\n"; 32 callbackInfo = callbackInfo + ",videoWidth:" + to_string(videoWidth) 33 + ",videoHeight:" + to_string(videoHeight) + ",videoDuration:" 34 + to_string(videoDuration) + "\n"; 35 PostStatusMessage(callbackInfo.c_str()); 36 } else if (strncmp(mime, "audio/", 6) == 0 && audioTrackIndex == -1) { 37 audioTrackIndex = i; 38 hasAudio = true; 39 40 // 获取音频格式信息 41 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &audioSampleRate); 42 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &audioChannelCount); 43 44 LOGI("Audio track: sampleRate=%d, channels=%d", 45 audioSampleRate, audioChannelCount); 46 47 LOGI("Selected audio track: %d", audioTrackIndex); 48 callbackInfo = 49 "Selected audio track:" + to_string(audioTrackIndex) + "\n"; 50 callbackInfo = callbackInfo + ",audioSampleRate:" + to_string(audioSampleRate) 51 + ",audioChannelCount:" + to_string(audioChannelCount) + "\n"; 52 PostStatusMessage(callbackInfo.c_str()); 53 } 54 } 55 56 AMediaFormat_delete(format); 57 } 58 59 return hasVideo || hasAudio; 60}
3.初始化解码器:
——> 切换Extractor音视频的轨道,AMediaExtractor_getTrackFormat(extractor, videoTrackIndex) 方法中获取AMediaFormat。
——> 通过AMediaCodec_createDecoderByType创建AMediaCodec
,并设置编解码AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder)。
以下是初始化编解码相关代码:
1// 初始化编解码器 2bool MediaExtratorDecodec::initDecodec(bool asyncMode) { 3 4 // 添加视频轨道 5 if (hasVideo) { 6 AMediaExtractor_selectTrack(extractor, videoTrackIndex); 7 mVideoFormat = AMediaExtractor_getTrackFormat(extractor, videoTrackIndex); 8 AMediaFormat_getString(mVideoFormat, AMEDIAFORMAT_KEY_MIME, &video_mime); 9 LOGI("video_mime: %s", video_mime); 10 callbackInfo = 11 "video_mime:" + string(video_mime) + "\n"; 12 PostStatusMessage(callbackInfo.c_str()); 13 14 mVideoCodec = createMediaCodec(mVideoFormat, video_mime, "", false /*isEncoder*/); 15 16 if (!mVideoCodec) { 17 LOGE("Failed to create video codec"); 18 callbackInfo = 19 "Failed to create video codec \n"; 20 PostStatusMessage(callbackInfo.c_str()); 21 return false; 22 } 23 24 if (asyncMode) { 25 AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB, 26 OnFormatChangedCB, OnErrorCB}; 27 AMediaCodec_setAsyncNotifyCallback(mVideoCodec, aCB, this); 28 29 ThreadTask task = []() { 30 CallBackHandle(); 31 }; 32 33 g_threadManager->submitTask("video-decode-Thread", task, PRIORITY_NORMAL); 34 35 } 36 37 LOGI("create video codec success"); 38 callbackInfo = 39 "create video codec success: \n"; 40 PostStatusMessage(callbackInfo.c_str()); 41 AMediaCodec_start(mVideoCodec); 42 } 43 44 45 // 添加音频轨道 46 if (hasAudio) { 47 AMediaExtractor_selectTrack(extractor, audioTrackIndex); 48 mAudioFormat = AMediaExtractor_getTrackFormat(extractor, audioTrackIndex); 49 AMediaFormat_getString(mAudioFormat, AMEDIAFORMAT_KEY_MIME, &audio_mime); 50 LOGI("audio_mime: %s", audio_mime); 51 callbackInfo = 52 "audio_mime:" + string(audio_mime) + "\n"; 53 PostStatusMessage(callbackInfo.c_str()); 54 55 mAudioCodec = createMediaCodec(mAudioFormat, audio_mime, "", false /*isEncoder*/); 56 57 if (!mAudioCodec) { 58 LOGE("Failed to create audio codec"); 59 callbackInfo = 60 "Failed to create audio codec \n"; 61 PostStatusMessage(callbackInfo.c_str()); 62 return false; 63 } 64 65 if (asyncMode) { 66 AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB, 67 OnFormatChangedCB, OnErrorCB}; 68 AMediaCodec_setAsyncNotifyCallback(mAudioCodec, aCB, this); 69 70 ThreadTask task = []() { 71 CallBackHandle(); 72 }; 73 74 g_threadManager->submitTask("audio-decode-Thread", task, PRIORITY_NORMAL); 75 76 } 77 78 LOGI("create audio codec success"); 79 callbackInfo = 80 "create audio codec success: \n"; 81 PostStatusMessage(callbackInfo.c_str()); 82 AMediaCodec_start(mAudioCodec); 83 } 84 85 86 LOGI("initDecodec initialized successfully"); 87 callbackInfo = 88 "initDecodec initialized successfully \n"; 89 PostStatusMessage(callbackInfo.c_str()); 90 return true; 91} 92 93 94AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string codecName, 95 bool isEncoder) { 96 ALOGV("In %s", __func__); 97 if (!mime) { 98 ALOGE("Please specify a mime type to create codec"); 99 return nullptr; 100 } 101 102 AMediaCodec *codec; 103 if (!codecName.empty()) { 104 codec = AMediaCodec_createCodecByName(codecName.c_str()); 105 if (!codec) { 106 ALOGE("Unable to create codec by name: %s", codecName.c_str()); 107 return nullptr; 108 } 109 ALOGV("create codec by name: %s", codecName.c_str()); 110 } else { 111 if (isEncoder) { 112 codec = AMediaCodec_createEncoderByType(mime); 113 } else { 114 codec = AMediaCodec_createDecoderByType(mime); 115 } 116 if (!codec) { 117 ALOGE("Unable to create codec by mime: %s", mime); 118 return nullptr; 119 } 120 char *out_name = nullptr; 121 AMediaCodec_getName(codec, &out_name); 122 ALOGV("create codec by mime: %s", out_name); 123 } 124 125 /* Configure codec with the given format*/ 126 const char *s = AMediaFormat_toString(format); 127 ALOGI("Input format: %s\n", s); 128 129 media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder); 130 if (status != AMEDIA_OK) { 131 ALOGE("AMediaCodec_configure failed %d", status); 132 return nullptr; 133 } 134 return codec; 135} 136
4.解码过程的操作:
解码过程解析:
——> 重新选择所有轨道以重置读取位置 AMediaExtractor_selectTrack(extractor, videoTrackIndex) 和 AMediaExtractor_selectTrack(extractor, audioTrackIndex);
——> 设置读取位置到开始 AMediaExtractor_seekTo(extractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
——> 遍历 获取下一个可用输入缓冲区的索引
ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mVideoCodec, kQueueDequeueTimeoutUs)
根据inIdx的状态值得到可用的输入:
onInputAvailable(mVideoCodec, inIdx);
——> 从提取器Extractor中获取到该样本的大小:
size_t bufSize = AMediaExtractor_getSampleSize(extractor);
——> 获取输入缓冲区,以备输入的样本数据的填充:
uint8_t *buf = AMediaCodec_getInputBuffer(mVideoCodec, bufIdx, &bufSize);
——> 从提取器读取数据包,填充至输入缓冲区中:
ssize_t bytesRead = AMediaExtractor_readSampleData(extractor, buf, bufSize);
——> 把缓冲区的buf送入解码器:
media_status_t status = AMediaCodec_queueInputBuffer(mVideoCodec, bufIdx, 0 /* offset */, bytesRead, presentationTimeUs, flag);
—— 遍历 获取下一个可用已处理数据缓冲区的索引:
ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mAudioCodec, &info, kQueueDequeueTimeoutUs);
根据outIdx的状态值得到可用的输入:
onOutputAvailable(mVideoCodec, outIdx, &info);
——> 获取输出缓冲区的数据:
uint8_t *buf = AMediaCodec_getOutputBuffer(mVideoCodec, bufIdx, &bufSize);
decodec():
选择对应的音视频轨道,从提取器中提取数据包进行解码的操作。
以下是解码的代码:
1// 执行解码 2bool MediaExtratorDecodec::decodec() { 3 LOGI("decodec==========="); 4 bool asyncMode = false; 5 6 AMediaCodecBufferInfo info; 7 bool sawEOS = false; 8 int64_t lastVideoPts = -1; 9 int64_t lastAudioPts = -1; 10 11 // 重新选择所有轨道以重置读取位置 12 if (hasVideo) AMediaExtractor_selectTrack(extractor, videoTrackIndex); 13 if (hasAudio) AMediaExtractor_selectTrack(extractor, audioTrackIndex); 14 15 // 设置读取位置到开始 16 AMediaExtractor_seekTo(extractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC); 17 18 while (!sawEOS) { 19 ssize_t trackIndex = AMediaExtractor_getSampleTrackIndex(extractor); 20 21 if (trackIndex < 0) { 22 sawEOS = true; 23 break; 24 } 25 26 if (trackIndex == videoTrackIndex && hasVideo) { 27 // 检查时间戳是否有效(避免重复或倒退的时间戳) 28 if (AMediaExtractor_getSampleTime(extractor) > lastVideoPts) { 29 if (!asyncMode) { 30 while (!mSawOutputEOS && !mSignalledError) { 31 /* Queue input data */ 32 if (!mSawInputEOS) { 33 ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mVideoCodec, 34 kQueueDequeueTimeoutUs); 35 if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) { 36 LOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", 37 inIdx); 38 mErrorCode = (media_status_t) inIdx; 39 return mErrorCode; 40 } else if (inIdx >= 0) { 41 onInputAvailable(mVideoCodec, inIdx); 42 } 43 } 44 45 /* Dequeue output data */ 46 AMediaCodecBufferInfo info; 47 ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mVideoCodec, &info, 48 kQueueDequeueTimeoutUs); 49 if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { 50 mVideoFormat = AMediaCodec_getOutputFormat(mVideoCodec); 51 const char *s = AMediaFormat_toString(mVideoFormat); 52 LOGI("Output format: %s\n", s); 53 } else if (outIdx >= 0) { 54 onOutputAvailable(mVideoCodec, outIdx, &info); 55 } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER || 56 outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) { 57 LOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", 58 outIdx); 59 mErrorCode = (media_status_t) outIdx; 60 return mErrorCode; 61 } 62 } 63 } else { 64 unique_lock<mutex> lock(mMutex); 65 mDecoderDoneCondition.wait(lock, [this]() { 66 return (mSawOutputEOS || mSignalledError); 67 }); 68 } 69 if (mSignalledError) { 70 LOGE("Received Error while Decoding"); 71 return mErrorCode; 72 } 73 74 lastVideoPts = info.presentationTimeUs; 75 } 76 } else if (trackIndex == audioTrackIndex && hasAudio) { //音频轨道的解码 77 // 检查时间戳是否有效 78 if (info.presentationTimeUs > lastAudioPts) { 79 // 检查时间戳是否有效(避免重复或倒退的时间戳) 80 if (!asyncMode) { 81 while (!mSawOutputEOS && !mSignalledError) { 82 /* Queue input data */ 83 if (!mSawInputEOS) { 84 ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mAudioCodec, 85 kQueueDequeueTimeoutUs); 86 if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) { 87 LOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", 88 inIdx); 89 mErrorCode = (media_status_t) inIdx; 90 return mErrorCode; 91 } else if (inIdx >= 0) { 92 onInputAvailable(mAudioCodec, inIdx); 93 } 94 } 95 96 /* Dequeue output data */ 97 AMediaCodecBufferInfo info; 98 ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mAudioCodec, &info, 99 kQueueDequeueTimeoutUs); 100 if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { 101 mAudioFormat = AMediaCodec_getOutputFormat(mAudioCodec); 102 const char *s = AMediaFormat_toString(mAudioFormat); 103 LOGI("Output format: %s\n", s); 104 } else if (outIdx >= 0) { 105 onOutputAvailable(mVideoCodec, outIdx, &info); 106 } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER || 107 outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) { 108 LOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", 109 outIdx); 110 mErrorCode = (media_status_t) outIdx; 111 return mErrorCode; 112 } 113 } 114 } else { 115 unique_lock<mutex> lock(mMutex); 116 mDecoderDoneCondition.wait(lock, [this]() { 117 return (mSawOutputEOS || mSignalledError); 118 }); 119 } 120 if (mSignalledError) { 121 ALOGE("Received Error while Decoding"); 122 return mErrorCode; 123 } 124 lastAudioPts = info.presentationTimeUs; 125 } 126 } 127 128 // 短暂休眠以避免过度占用CPU 129 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 130 } 131 132 LOGI("media decodec completed"); 133 callbackInfo = 134 "media decodec completed \n"; 135 PostStatusMessage(callbackInfo.c_str()); 136 return true; 137}
onInputAvailable():
可用输入时,对数据的操作:
1void MediaExtratorDecodec::onInputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx) { 2 LOGD("onInputAvailable %s", __func__); 3 if (mediaCodec == mVideoCodec && mediaCodec) { 4 if (mSawInputEOS || bufIdx < 0) return; 5 if (mSignalledError) { 6 CallBackHandle::mSawError = true; 7 mDecoderDoneCondition.notify_one(); 8 return; 9 } 10 11 size_t bufSize = AMediaExtractor_getSampleSize(extractor); 12 if (bufSize <= 0) { 13 LOGE("AMediaExtractor_getSampleSize===="); 14 return; 15 } 16 // 获取输入缓冲区 17 uint8_t *buf = AMediaCodec_getInputBuffer(mVideoCodec, bufIdx, &bufSize); 18 if (!buf) { 19 mErrorCode = AMEDIA_ERROR_IO; 20 mSignalledError = true; 21 mDecoderDoneCondition.notify_one(); 22 return; 23 } 24 25 // 从提取器读取数据 26 ssize_t bytesRead = AMediaExtractor_readSampleData(extractor, buf, bufSize); 27 if (bytesRead < 0) { 28 LOGI("reading video sample data: %zd", bytesRead); 29 // 输入结束 30 AMediaCodec_queueInputBuffer(mVideoCodec, bufIdx, 0, 0, 0, 31 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); 32 LOGI("从提取器读取数据到结束尾"); 33 callbackInfo = 34 "视频轨道从提取器读取数据到结束尾 reading sample data:" + to_string(bytesRead) + 35 "\n"; 36 PostStatusMessage(callbackInfo.c_str()); 37 return; 38 } 39 uint32_t flag = AMediaExtractor_getSampleFlags(extractor); 40 int64_t presentationTimeUs = AMediaExtractor_getSampleTime(extractor); 41 42 if (flag == AMEDIA_ERROR_MALFORMED) { 43 mErrorCode = (media_status_t) flag; 44 mSignalledError = true; 45 mDecoderDoneCondition.notify_one(); 46 return; 47 } 48 49 if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true; 50 LOGD("video - %s bytesRead : %zd presentationTimeUs : %" PRId64 " mSawInputEOS : %s", 51 __FUNCTION__, 52 bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE"); 53 54 // 将数据送入解码器 55 media_status_t status = AMediaCodec_queueInputBuffer(mVideoCodec, bufIdx, 0 /* offset */, 56 bytesRead, presentationTimeUs, flag); 57 if (AMEDIA_OK != status) { 58 mErrorCode = status; 59 mSignalledError = true; 60 mDecoderDoneCondition.notify_one(); 61 return; 62 } 63 64 if (!AMediaExtractor_advance(extractor)) { 65 return; 66 } 67 68 } else if (mediaCodec == mAudioCodec && mediaCodec) { 69 if (mSawInputEOS || bufIdx < 0) return; 70 if (mSignalledError) { 71 CallBackHandle::mSawError = true; 72 mDecoderDoneCondition.notify_one(); 73 return; 74 } 75 76 size_t bufSize = AMediaExtractor_getSampleSize(extractor); 77 if (bufSize <= 0) { 78 LOGE("AMediaExtractor_getSampleSize===="); 79 return; 80 } 81 // 获取输入缓冲区 82 uint8_t *buf = AMediaCodec_getInputBuffer(mAudioCodec, bufIdx, &bufSize); 83 if (!buf) { 84 mErrorCode = AMEDIA_ERROR_IO; 85 mSignalledError = true; 86 mDecoderDoneCondition.notify_one(); 87 return; 88 } 89 90 // 从提取器读取数据 91 ssize_t bytesRead = AMediaExtractor_readSampleData(extractor, buf, bufSize); 92 if (bytesRead < 0) { 93 LOGI("reading audio sample data: %zd", bytesRead); 94 // 输入结束 95 AMediaCodec_queueInputBuffer(mAudioCodec, bufIdx, 0, 0, 0, 96 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); 97 LOGI("从提取器读取音频轨道数据到结束尾"); 98 callbackInfo = 99 "音频轨道从提取器读取数据到结束尾 reading sample data:" + to_string(bytesRead) + 100 "\n"; 101 PostStatusMessage(callbackInfo.c_str()); 102 return; 103 } 104 uint32_t flag = AMediaExtractor_getSampleFlags(extractor); 105 int64_t presentationTimeUs = AMediaExtractor_getSampleTime(extractor); 106 107 if (flag == AMEDIA_ERROR_MALFORMED) { 108 mErrorCode = (media_status_t) flag; 109 mSignalledError = true; 110 mDecoderDoneCondition.notify_one(); 111 return; 112 } 113 114 if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true; 115 LOGD("audio - %s bytesRead : %zd presentationTimeUs : %" PRId64 " mSawInputEOS : %s", 116 __FUNCTION__, 117 bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE"); 118 119 // 将数据送入解码器 120 media_status_t status = AMediaCodec_queueInputBuffer(mAudioCodec, bufIdx, 0 /* offset */, 121 bytesRead, presentationTimeUs, flag); 122 if (AMEDIA_OK != status) { 123 mErrorCode = status; 124 mSignalledError = true; 125 mDecoderDoneCondition.notify_one(); 126 return; 127 } 128 129 if (!AMediaExtractor_advance(extractor)) { 130 return; 131 } 132 133 } 134 135 136} 137
onOutputAvailable():
当有可用的输出,对数据的操作:
1void MediaExtratorDecodec::onOutputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx, 2 AMediaCodecBufferInfo *bufferInfo) { 3 LOGD("In %s", __func__); 4 if (mediaCodec == mVideoCodec && mediaCodec) { 5 if (mSawOutputEOS || bufIdx < 0) return; 6 if (mSignalledError) { 7 CallBackHandle::mSawError = true; 8 mDecoderDoneCondition.notify_one(); 9 return; 10 } 11 12 if (mOutFp != nullptr) { 13 size_t bufSize; 14 uint8_t *buf = AMediaCodec_getOutputBuffer(mVideoCodec, bufIdx, &bufSize); 15 if (buf) { 16 fwrite(buf, sizeof(char), bufferInfo->size, mOutFp); 17 LOGD("bytes written into file %d\n", bufferInfo->size); 18 } 19 } 20 21 AMediaCodec_releaseOutputBuffer(mVideoCodec, bufIdx, false); 22 mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)); 23 mNumOutputVideoFrame++; 24 LOGD("video - %s index : %d mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx, 25 mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputVideoFrame); 26 27 if (mSawOutputEOS) { 28 CallBackHandle::mIsDone = true; 29 mDecoderDoneCondition.notify_one(); 30 } 31 } else if (mediaCodec == mAudioCodec && mediaCodec) { 32 if (mSawOutputEOS || bufIdx < 0) return; 33 if (mSignalledError) { 34 CallBackHandle::mSawError = true; 35 mDecoderDoneCondition.notify_one(); 36 return; 37 } 38 39 if (mOutFp != nullptr) { 40 size_t bufSize; 41 uint8_t *buf = AMediaCodec_getOutputBuffer(mAudioCodec, bufIdx, &bufSize); 42 if (buf) { 43 fwrite(buf, sizeof(char), bufferInfo->size, mOutFp); 44 LOGD("bytes written into file %d\n", bufferInfo->size); 45 } 46 } 47 48 AMediaCodec_releaseOutputBuffer(mAudioCodec, bufIdx, false); 49 mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)); 50 mNumOutputAudioFrame++; 51 LOGD("video - %s index : %d mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx, 52 mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputAudioFrame); 53 54 if (mSawOutputEOS) { 55 CallBackHandle::mIsDone = true; 56 mDecoderDoneCondition.notify_one(); 57 } 58 } 59}
5.完整的编解码的代码:
以上的代码放在本人的GitHub项目中:https://github.com/wangyongyao1989/FFmpegPractices
中的MediaExtratorDecodec.cpp:
1// Author : wangyongyao https://github.com/wangyongyao1989 2// Created by MMM on 2025/9/29. 3// 4 5#include <sys/stat.h> 6#include "includes/MediaExtratorDecodec.h" 7 8 9MediaExtratorDecodec::MediaExtratorDecodec(JNIEnv *env, jobject thiz) { 10 mEnv = env; 11 env->GetJavaVM(&mJavaVm); 12 mJavaObj = env->NewGlobalRef(thiz); 13 // 初始化线程池 14 g_threadManager = std::make_unique<AndroidThreadManager>(); 15 ThreadPoolConfig config; 16 config.minThreads = 2; 17 config.maxThreads = 4; 18 config.idleTimeoutMs = 30000; 19 config.queueSize = 50; 20 g_threadManager->initThreadPool(config); 21 22} 23 24MediaExtratorDecodec::~MediaExtratorDecodec() { 25 mEnv->DeleteGlobalRef(mJavaObj); 26 if (mEnv) { 27 mEnv = nullptr; 28 } 29 30 if (mJavaVm) { 31 mJavaVm = nullptr; 32 } 33 34 if (mJavaObj) { 35 mJavaObj = nullptr; 36 } 37 38 release(); 39 g_threadManager.reset(); 40} 41 42void 43MediaExtratorDecodec::startMediaExtratorDecodec(const char *inputPath) { 44 sSrcPath = inputPath; 45 46 LOGI("sSrcPath :%s \n ", sSrcPath.c_str()); 47 callbackInfo = 48 "sSrcPath:" + sSrcPath + "\n"; 49 PostStatusMessage(callbackInfo.c_str()); 50 // 1. 初始化提取器 51 if (!initExtractor()) { 52 LOGE("Failed to initialize extractor"); 53 callbackInfo = 54 "Failed to initialize extractor \n"; 55 PostStatusMessage(callbackInfo.c_str()); 56 return; 57 } 58 59 // 2. 选择轨道 60 if (!selectTracksAndGetFormat()) { 61 LOGE("No valid tracks found"); 62 callbackInfo = 63 "No valid tracks found \n"; 64 PostStatusMessage(callbackInfo.c_str()); 65 return; 66 } 67 68 // 3. 初始化解码器 69 if (!initDecodec(false)) { 70 LOGE("Failed to initialize Decodec"); 71 callbackInfo = 72 "Failed to initialize Decodec \n"; 73 PostStatusMessage(callbackInfo.c_str()); 74 return; 75 } 76 77 // 4. 执行解码 78 if (!decodec()) { 79 LOGE("Decodec failed"); 80 callbackInfo = 81 "Decodec failed \n"; 82 PostStatusMessage(callbackInfo.c_str()); 83 return; 84 } 85 86 // 释放资源 87 release(); 88} 89 90// 初始化提取器 91bool MediaExtratorDecodec::initExtractor() { 92 93 extractor = AMediaExtractor_new(); 94 if (!extractor) { 95 LOGE("Failed to create media extractor "); 96 callbackInfo = 97 "Failed to create media extractor \n"; 98 PostStatusMessage(callbackInfo.c_str()); 99 return false; 100 } 101 LOGE("inputPath:%s", sSrcPath.c_str()); 102 FILE *inputFp = fopen(sSrcPath.c_str(), "rb"); 103 if (!inputFp) { 104 LOGE("Unable to open output file :%s", sSrcPath.c_str()); 105 callbackInfo = 106 "Unable to open output file :" + sSrcPath + "\n"; 107 PostStatusMessage(callbackInfo.c_str()); 108 return false; 109 } 110 struct stat buf; 111 stat(sSrcPath.c_str(), &buf); 112 size_t fileSize = buf.st_size; 113 int32_t input_fd = fileno(inputFp); 114 115 LOGE("input_fd:%d", input_fd); 116 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, input_fd, 0, fileSize); 117 if (status != AMEDIA_OK) { 118 LOGE("Failed to set data source: %d", status); 119 callbackInfo = 120 "Failed to set data source :" + to_string(status) + "\n"; 121 PostStatusMessage(callbackInfo.c_str()); 122 return false; 123 } 124 125 LOGI("Extractor initialized successfully"); 126 return true; 127} 128 129// 选择轨道获取AMediaFormat 130bool MediaExtratorDecodec::selectTracksAndGetFormat() { 131 LOGI("selectTracksAndGetFormat==========="); 132 133 size_t trackCount = AMediaExtractor_getTrackCount(extractor); 134 LOGI("Total tracks: %zu", trackCount); 135 callbackInfo = 136 "Total tracks:" + to_string(trackCount) + "\n"; 137 PostStatusMessage(callbackInfo.c_str()); 138 139 for (size_t i = 0; i < trackCount; i++) { 140 AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, i); 141 if (!format) continue; 142 143 const char *mime; 144 if (AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) { 145 LOGI("Track %zu: MIME=%s", i, mime); 146 147 if (strncmp(mime, "video/", 6) == 0 && videoTrackIndex == -1) { 148 videoTrackIndex = i; 149 hasVideo = true; 150 // 获取视频格式信息 151 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &videoWidth); 152 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &videoHeight); 153 AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &videoDuration); 154 LOGI("Selected video track: %d", videoTrackIndex); 155 156 LOGI("Video track: %dx%d, duration: %lld us", 157 videoWidth, videoHeight, videoDuration); 158 callbackInfo = 159 "Selected video track:" + to_string(videoTrackIndex) + "\n"; 160 callbackInfo = callbackInfo + ",videoWidth:" + to_string(videoWidth) 161 + ",videoHeight:" + to_string(videoHeight) + ",videoDuration:" 162 + to_string(videoDuration) + "\n"; 163 PostStatusMessage(callbackInfo.c_str()); 164 } else if (strncmp(mime, "audio/", 6) == 0 && audioTrackIndex == -1) { 165 audioTrackIndex = i; 166 hasAudio = true; 167 168 // 获取音频格式信息 169 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &audioSampleRate); 170 AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &audioChannelCount); 171 172 LOGI("Audio track: sampleRate=%d, channels=%d", 173 audioSampleRate, audioChannelCount); 174 175 LOGI("Selected audio track: %d", audioTrackIndex); 176 callbackInfo = 177 "Selected audio track:" + to_string(audioTrackIndex) + "\n"; 178 callbackInfo = callbackInfo + ",audioSampleRate:" + to_string(audioSampleRate) 179 + ",audioChannelCount:" + to_string(audioChannelCount) + "\n"; 180 PostStatusMessage(callbackInfo.c_str()); 181 } 182 } 183 184 AMediaFormat_delete(format); 185 } 186 187 return hasVideo || hasAudio; 188} 189 190// 初始化复用器 191bool MediaExtratorDecodec::initDecodec(bool asyncMode) { 192 193 // 添加视频轨道 194 if (hasVideo) { 195 AMediaExtractor_selectTrack(extractor, videoTrackIndex); 196 mVideoFormat = AMediaExtractor_getTrackFormat(extractor, videoTrackIndex); 197 AMediaFormat_getString(mVideoFormat, AMEDIAFORMAT_KEY_MIME, &video_mime); 198 LOGI("video_mime: %s", video_mime); 199 callbackInfo = 200 "video_mime:" + string(video_mime) + "\n"; 201 PostStatusMessage(callbackInfo.c_str()); 202 203 mVideoCodec = createMediaCodec(mVideoFormat, video_mime, "", false /*isEncoder*/); 204 205 if (!mVideoCodec) { 206 LOGE("Failed to create video codec"); 207 callbackInfo = 208 "Failed to create video codec \n"; 209 PostStatusMessage(callbackInfo.c_str()); 210 return false; 211 } 212 213 if (asyncMode) { 214 AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB, 215 OnFormatChangedCB, OnErrorCB}; 216 AMediaCodec_setAsyncNotifyCallback(mVideoCodec, aCB, this); 217 218 ThreadTask task = []() { 219 CallBackHandle(); 220 }; 221 222 g_threadManager->submitTask("video-decode-Thread", task, PRIORITY_NORMAL); 223 224 } 225 226 LOGI("create video codec success"); 227 callbackInfo = 228 "create video codec success: \n"; 229 PostStatusMessage(callbackInfo.c_str()); 230 AMediaCodec_start(mVideoCodec); 231 } 232 233 234 // 添加音频轨道 235 if (hasAudio) { 236 AMediaExtractor_selectTrack(extractor, audioTrackIndex); 237 mAudioFormat = AMediaExtractor_getTrackFormat(extractor, audioTrackIndex); 238 AMediaFormat_getString(mAudioFormat, AMEDIAFORMAT_KEY_MIME, &audio_mime); 239 LOGI("audio_mime: %s", audio_mime); 240 callbackInfo = 241 "audio_mime:" + string(audio_mime) + "\n"; 242 PostStatusMessage(callbackInfo.c_str()); 243 244 mAudioCodec = createMediaCodec(mAudioFormat, audio_mime, "", false /*isEncoder*/); 245 246 if (!mAudioCodec) { 247 LOGE("Failed to create audio codec"); 248 callbackInfo = 249 "Failed to create audio codec \n"; 250 PostStatusMessage(callbackInfo.c_str()); 251 return false; 252 } 253 254 if (asyncMode) { 255 AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB, 256 OnFormatChangedCB, OnErrorCB}; 257 AMediaCodec_setAsyncNotifyCallback(mAudioCodec, aCB, this); 258 259 ThreadTask task = []() { 260 CallBackHandle(); 261 }; 262 263 g_threadManager->submitTask("audio-decode-Thread", task, PRIORITY_NORMAL); 264 265 } 266 267 LOGI("create audio codec success"); 268 callbackInfo = 269 "create audio codec success: \n"; 270 PostStatusMessage(callbackInfo.c_str()); 271 AMediaCodec_start(mAudioCodec); 272 } 273 274 275 LOGI("initDecodec initialized successfully"); 276 callbackInfo = 277 "initDecodec initialized successfully \n"; 278 PostStatusMessage(callbackInfo.c_str()); 279 return true; 280} 281 282 283// 执行解码 284bool MediaExtratorDecodec::decodec() { 285 LOGI("decodec==========="); 286 bool asyncMode = false; 287 288 AMediaCodecBufferInfo info; 289 bool sawEOS = false; 290 int64_t lastVideoPts = -1; 291 int64_t lastAudioPts = -1; 292 293 // 重新选择所有轨道以重置读取位置 294 if (hasVideo) AMediaExtractor_selectTrack(extractor, videoTrackIndex); 295 if (hasAudio) AMediaExtractor_selectTrack(extractor, audioTrackIndex); 296 297 // 设置读取位置到开始 298 AMediaExtractor_seekTo(extractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC); 299 300 while (!sawEOS) { 301 ssize_t trackIndex = AMediaExtractor_getSampleTrackIndex(extractor); 302 303 if (trackIndex < 0) { 304 sawEOS = true; 305 break; 306 } 307 308 if (trackIndex == videoTrackIndex && hasVideo) { 309 // 检查时间戳是否有效(避免重复或倒退的时间戳) 310 if (AMediaExtractor_getSampleTime(extractor) > lastVideoPts) { 311 if (!asyncMode) { 312 while (!mSawOutputEOS && !mSignalledError) { 313 /* Queue input data */ 314 if (!mSawInputEOS) { 315 ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mVideoCodec, 316 kQueueDequeueTimeoutUs); 317 if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) { 318 LOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", 319 inIdx); 320 mErrorCode = (media_status_t) inIdx; 321 return mErrorCode; 322 } else if (inIdx >= 0) { 323 onInputAvailable(mVideoCodec, inIdx); 324 } 325 } 326 327 /* Dequeue output data */ 328 AMediaCodecBufferInfo info; 329 ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mVideoCodec, &info, 330 kQueueDequeueTimeoutUs); 331 if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { 332 mVideoFormat = AMediaCodec_getOutputFormat(mVideoCodec); 333 const char *s = AMediaFormat_toString(mVideoFormat); 334 LOGI("Output format: %s\n", s); 335 } else if (outIdx >= 0) { 336 onOutputAvailable(mVideoCodec, outIdx, &info); 337 } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER || 338 outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) { 339 LOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", 340 outIdx); 341 mErrorCode = (media_status_t) outIdx; 342 return mErrorCode; 343 } 344 } 345 } else { 346 unique_lock<mutex> lock(mMutex); 347 mDecoderDoneCondition.wait(lock, [this]() { 348 return (mSawOutputEOS || mSignalledError); 349 }); 350 } 351 if (mSignalledError) { 352 LOGE("Received Error while Decoding"); 353 return mErrorCode; 354 } 355 356 lastVideoPts = info.presentationTimeUs; 357 } 358 } else if (trackIndex == audioTrackIndex && hasAudio) { //音频轨道的解码 359 // 检查时间戳是否有效 360 if (info.presentationTimeUs > lastAudioPts) { 361 // 检查时间戳是否有效(避免重复或倒退的时间戳) 362 if (!asyncMode) { 363 while (!mSawOutputEOS && !mSignalledError) { 364 /* Queue input data */ 365 if (!mSawInputEOS) { 366 ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mAudioCodec, 367 kQueueDequeueTimeoutUs); 368 if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) { 369 LOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", 370 inIdx); 371 mErrorCode = (media_status_t) inIdx; 372 return mErrorCode; 373 } else if (inIdx >= 0) { 374 onInputAvailable(mAudioCodec, inIdx); 375 } 376 } 377 378 /* Dequeue output data */ 379 AMediaCodecBufferInfo info; 380 ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mAudioCodec, &info, 381 kQueueDequeueTimeoutUs); 382 if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { 383 mAudioFormat = AMediaCodec_getOutputFormat(mAudioCodec); 384 const char *s = AMediaFormat_toString(mAudioFormat); 385 LOGI("Output format: %s\n", s); 386 } else if (outIdx >= 0) { 387 onOutputAvailable(mVideoCodec, outIdx, &info); 388 } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER || 389 outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) { 390 LOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", 391 outIdx); 392 mErrorCode = (media_status_t) outIdx; 393 return mErrorCode; 394 } 395 } 396 } else { 397 unique_lock<mutex> lock(mMutex); 398 mDecoderDoneCondition.wait(lock, [this]() { 399 return (mSawOutputEOS || mSignalledError); 400 }); 401 } 402 if (mSignalledError) { 403 ALOGE("Received Error while Decoding"); 404 return mErrorCode; 405 } 406 lastAudioPts = info.presentationTimeUs; 407 } 408 } 409 410 // 短暂休眠以避免过度占用CPU 411 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 412 } 413 414 LOGI("media decodec completed"); 415 callbackInfo = 416 "media decodec completed \n"; 417 PostStatusMessage(callbackInfo.c_str()); 418 return true; 419} 420 421 422// 释放资源 423void MediaExtratorDecodec::release() { 424 425 if (extractor) { 426 AMediaExtractor_delete(extractor); 427 extractor = nullptr; 428 } 429 430 if (mVideoFormat) { 431 AMediaFormat_delete(mVideoFormat); 432 mVideoFormat = nullptr; 433 } 434 435 if (mVideoCodec) { 436 AMediaCodec_stop(mVideoCodec); 437 AMediaCodec_delete(mVideoCodec); 438 } 439 440 if (mAudioCodec) { 441 AMediaCodec_stop(mAudioCodec); 442 AMediaCodec_delete(mAudioCodec); 443 } 444 if (mAudioFormat) { 445 AMediaFormat_delete(mAudioFormat); 446 mAudioFormat = nullptr; 447 } 448 LOGI("Resources released"); 449} 450 451void MediaExtratorDecodec::onInputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx) { 452 LOGD("onInputAvailable %s", __func__); 453 if (mediaCodec == mVideoCodec && mediaCodec) { 454 if (mSawInputEOS || bufIdx < 0) return; 455 if (mSignalledError) { 456 CallBackHandle::mSawError = true; 457 mDecoderDoneCondition.notify_one(); 458 return; 459 } 460 461 size_t bufSize = AMediaExtractor_getSampleSize(extractor); 462 if (bufSize <= 0) { 463 LOGE("AMediaExtractor_getSampleSize===="); 464 return; 465 } 466 // 获取输入缓冲区 467 uint8_t *buf = AMediaCodec_getInputBuffer(mVideoCodec, bufIdx, &bufSize); 468 if (!buf) { 469 mErrorCode = AMEDIA_ERROR_IO; 470 mSignalledError = true; 471 mDecoderDoneCondition.notify_one(); 472 return; 473 } 474 475 // 从提取器读取数据 476 ssize_t bytesRead = AMediaExtractor_readSampleData(extractor, buf, bufSize); 477 if (bytesRead < 0) { 478 LOGI("reading video sample data: %zd", bytesRead); 479 // 输入结束 480 AMediaCodec_queueInputBuffer(mVideoCodec, bufIdx, 0, 0, 0, 481 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); 482 LOGI("从提取器读取数据到结束尾"); 483 callbackInfo = 484 "视频轨道从提取器读取数据到结束尾 reading sample data:" + to_string(bytesRead) + 485 "\n"; 486 PostStatusMessage(callbackInfo.c_str()); 487 return; 488 } 489 uint32_t flag = AMediaExtractor_getSampleFlags(extractor); 490 int64_t presentationTimeUs = AMediaExtractor_getSampleTime(extractor); 491 492 if (flag == AMEDIA_ERROR_MALFORMED) { 493 mErrorCode = (media_status_t) flag; 494 mSignalledError = true; 495 mDecoderDoneCondition.notify_one(); 496 return; 497 } 498 499 if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true; 500 LOGD("video - %s bytesRead : %zd presentationTimeUs : %" PRId64 " mSawInputEOS : %s", 501 __FUNCTION__, 502 bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE"); 503 504 // 将数据送入解码器 505 media_status_t status = AMediaCodec_queueInputBuffer(mVideoCodec, bufIdx, 0 /* offset */, 506 bytesRead, presentationTimeUs, flag); 507 if (AMEDIA_OK != status) { 508 mErrorCode = status; 509 mSignalledError = true; 510 mDecoderDoneCondition.notify_one(); 511 return; 512 } 513 514 if (!AMediaExtractor_advance(extractor)) { 515 return; 516 } 517 518 } else if (mediaCodec == mAudioCodec && mediaCodec) { 519 if (mSawInputEOS || bufIdx < 0) return; 520 if (mSignalledError) { 521 CallBackHandle::mSawError = true; 522 mDecoderDoneCondition.notify_one(); 523 return; 524 } 525 526 size_t bufSize = AMediaExtractor_getSampleSize(extractor); 527 if (bufSize <= 0) { 528 LOGE("AMediaExtractor_getSampleSize===="); 529 return; 530 } 531 // 获取输入缓冲区 532 uint8_t *buf = AMediaCodec_getInputBuffer(mAudioCodec, bufIdx, &bufSize); 533 if (!buf) { 534 mErrorCode = AMEDIA_ERROR_IO; 535 mSignalledError = true; 536 mDecoderDoneCondition.notify_one(); 537 return; 538 } 539 540 // 从提取器读取数据 541 ssize_t bytesRead = AMediaExtractor_readSampleData(extractor, buf, bufSize); 542 if (bytesRead < 0) { 543 LOGI("reading audio sample data: %zd", bytesRead); 544 // 输入结束 545 AMediaCodec_queueInputBuffer(mAudioCodec, bufIdx, 0, 0, 0, 546 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); 547 LOGI("从提取器读取音频轨道数据到结束尾"); 548 callbackInfo = 549 "音频轨道从提取器读取数据到结束尾 reading sample data:" + to_string(bytesRead) + 550 "\n"; 551 PostStatusMessage(callbackInfo.c_str()); 552 return; 553 } 554 uint32_t flag = AMediaExtractor_getSampleFlags(extractor); 555 int64_t presentationTimeUs = AMediaExtractor_getSampleTime(extractor); 556 557 if (flag == AMEDIA_ERROR_MALFORMED) { 558 mErrorCode = (media_status_t) flag; 559 mSignalledError = true; 560 mDecoderDoneCondition.notify_one(); 561 return; 562 } 563 564 if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true; 565 LOGD("audio - %s bytesRead : %zd presentationTimeUs : %" PRId64 " mSawInputEOS : %s", 566 __FUNCTION__, 567 bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE"); 568 569 // 将数据送入解码器 570 media_status_t status = AMediaCodec_queueInputBuffer(mAudioCodec, bufIdx, 0 /* offset */, 571 bytesRead, presentationTimeUs, flag); 572 if (AMEDIA_OK != status) { 573 mErrorCode = status; 574 mSignalledError = true; 575 mDecoderDoneCondition.notify_one(); 576 return; 577 } 578 579 if (!AMediaExtractor_advance(extractor)) { 580 return; 581 } 582 583 } 584 585 586} 587 588 589void MediaExtratorDecodec::onOutputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx, 590 AMediaCodecBufferInfo *bufferInfo) { 591 LOGD("In %s", __func__); 592 if (mediaCodec == mVideoCodec && mediaCodec) { 593 if (mSawOutputEOS || bufIdx < 0) return; 594 if (mSignalledError) { 595 CallBackHandle::mSawError = true; 596 mDecoderDoneCondition.notify_one(); 597 return; 598 } 599 600 if (mOutFp != nullptr) { 601 size_t bufSize; 602 uint8_t *buf = AMediaCodec_getOutputBuffer(mVideoCodec, bufIdx, &bufSize); 603 if (buf) { 604 fwrite(buf, sizeof(char), bufferInfo->size, mOutFp); 605 LOGD("bytes written into file %d\n", bufferInfo->size); 606 } 607 } 608 609 AMediaCodec_releaseOutputBuffer(mVideoCodec, bufIdx, false); 610 mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)); 611 mNumOutputVideoFrame++; 612 LOGD("video - %s index : %d mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx, 613 mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputVideoFrame); 614 615 if (mSawOutputEOS) { 616 CallBackHandle::mIsDone = true; 617 mDecoderDoneCondition.notify_one(); 618 } 619 } else if (mediaCodec == mAudioCodec && mediaCodec) { 620 if (mSawOutputEOS || bufIdx < 0) return; 621 if (mSignalledError) { 622 CallBackHandle::mSawError = true; 623 mDecoderDoneCondition.notify_one(); 624 return; 625 } 626 627 if (mOutFp != nullptr) { 628 size_t bufSize; 629 uint8_t *buf = AMediaCodec_getOutputBuffer(mAudioCodec, bufIdx, &bufSize); 630 if (buf) { 631 fwrite(buf, sizeof(char), bufferInfo->size, mOutFp); 632 LOGD("bytes written into file %d\n", bufferInfo->size); 633 } 634 } 635 636 AMediaCodec_releaseOutputBuffer(mAudioCodec, bufIdx, false); 637 mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)); 638 mNumOutputAudioFrame++; 639 LOGD("video - %s index : %d mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx, 640 mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputAudioFrame); 641 642 if (mSawOutputEOS) { 643 CallBackHandle::mIsDone = true; 644 mDecoderDoneCondition.notify_one(); 645 } 646 } 647} 648 649void MediaExtratorDecodec::onFormatChanged(AMediaCodec *mediaCodec, AMediaFormat *format) { 650 LOGD("In %s", __func__); 651 if (mediaCodec == mVideoCodec && mediaCodec) { 652 LOGD("%s { %s }", __FUNCTION__, AMediaFormat_toString(format)); 653 mVideoFormat = format; 654 } 655 if (mediaCodec == mAudioCodec && mediaCodec) { 656 LOGD("%s { %s }", __FUNCTION__, AMediaFormat_toString(format)); 657 mAudioFormat = format; 658 } 659} 660 661void MediaExtratorDecodec::onError(AMediaCodec *mediaCodec, media_status_t err) { 662 LOGD("In %s", __func__); 663 if (mediaCodec == mVideoCodec && mediaCodec) { 664 ALOGE("Received Error %d", err); 665 mErrorCode = err; 666 mSignalledError = true; 667 mDecoderDoneCondition.notify_one(); 668 } 669} 670 671 672JNIEnv *MediaExtratorDecodec::GetJNIEnv(bool *isAttach) { 673 JNIEnv *env; 674 int status; 675 if (nullptr == mJavaVm) { 676 LOGD("GetJNIEnv mJavaVm == nullptr"); 677 return nullptr; 678 } 679 *isAttach = false; 680 status = mJavaVm->GetEnv((void **) &env, JNI_VERSION_1_6); 681 if (status != JNI_OK) { 682 status = mJavaVm->AttachCurrentThread(&env, nullptr); 683 if (status != JNI_OK) { 684 LOGD("GetJNIEnv failed to attach current thread"); 685 return nullptr; 686 } 687 *isAttach = true; 688 } 689 return env; 690} 691 692void MediaExtratorDecodec::PostStatusMessage(const char *msg) { 693 bool isAttach = false; 694 JNIEnv *pEnv = GetJNIEnv(&isAttach); 695 if (pEnv == nullptr) { 696 return; 697 } 698 jobject javaObj = mJavaObj; 699 jmethodID mid = pEnv->GetMethodID(pEnv->GetObjectClass(javaObj), "CppStatusCallback", 700 "(Ljava/lang/String;)V"); 701 jstring pJstring = pEnv->NewStringUTF(msg); 702 pEnv->CallVoidMethod(javaObj, mid, pJstring); 703 if (isAttach) { 704 JavaVM *pJavaVm = mJavaVm; 705 pJavaVm->DetachCurrentThread(); 706 } 707} 708 709
三.FFmpeg/MediaCodec解码过程对比:
FFmpeg:
——> 打开输入文件获取输入文件的封装实例AVFormatContext :avformat_open_input(&in_fmt_ctx, srcPath, nullptr, nullptr);
——> 查找音视频文件中的流信息:avformat_find_stream_info(in_fmt_ctx, nullptr);
——> 分别查找音频和视频的索引:av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0) / av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0) ;
——> 分别查找流封装的实例AVStream:src_video = in_fmt_ctx->streams[video_index]
/ src_audio = in_fmt_ctx->streams[audio_index] ;
——> 这里是从媒体文件中封装实例AVFormatContext,查找音视频文件中的流信息 avformat_find_stream_info(in_fmt_ctx, nullptr)
——> 从封装实例中查找视频流的索引video_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
——> 查找视频流AVStream,src_video = in_fmt_ctx->streams[video_index]。
——> 根据编解码器ID,查找解码器AVCodec *video_codec = (AVCodec *) avcodec_find_decoder(video_codec_id);
——> 把未解压的数据包发给解码器实例avcodec_send_packet(video_decode_ctx, packet);
——> 从解码器实例获取还原后的数据帧avcodec_receive_frame(video_decode_ctx, frame);
MediaCodec:
——> 创建 AMediaExtractor_new()
——> 设置Extractor的fd AMediaExtractor_setDataSourceFd(extractor, input_fd, 0, fileSize)
——> AMediaExtractor_getTrackCount()获取取器Extractor中的轨道数
——> AMediaExtractor_getTrackFormat(extractor, i) 遍历轨道从中获取每个轨道中的AMediaFormat
——> 筛选出音频轨道和视频轨道,分别获取音频轨道的audioTrackIndex值和视频轨道videoTrackIndex值。
——> 切换Extractor音视频的轨道,AMediaExtractor_getTrackFormat(extractor, videoTrackIndex) 方法中获取AMediaFormat。
——> 通过AMediaCodec_createDecoderByType创建AMediaCodec
,并设置编解码AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder)。
——> 重新选择所有轨道以重置读取位置 AMediaExtractor_selectTrack(extractor, videoTrackIndex) 和 AMediaExtractor_selectTrack(extractor, audioTrackIndex);
——> 设置读取位置到开始 AMediaExtractor_seekTo(extractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
——>遍历可用的输入和可用的输出
四.效果展示:

以上的代码放在本人的GitHub项目:https://github.com/wangyongyao1989/FFmpegPractices
《音视频编解码全流程之用Extractor后Decodec》 是转载文章,点击查看原文。