音视频编解码全流程之用Extractor后Decodec

作者:Everbrilliant89日期:10/1/2025

系列文章:

音视频编解码全流程之提取器Extractor

音视频编解码全流程之复用器Muxer

音视频编解码全流程之用Extractor后Muxer生成MP4


根据前面叙述的 音视频编解码全流程之Extractor 可知:

媒体文件提取数据包的初始化及提取流程;

依此为基础现在本篇文章中实现“从媒体文件中Extractor提取数据包后,对提取后的数据进行解码的操作”。

一 .FFmpeg中提取媒体文件数据包后,对进行数据包解码:

1.FFmpeg交叉编译:

首先的是FFmpeg交叉编译,在之前的博客中有介绍交叉编译的全过程,感兴趣的可以查看博客:

Android Liunx ffmpeg交叉编译

macOs上交叉编译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提供的下层编解码芯片的接口,在NdkMediaCodecAMediaCodec_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》 是转载文章,点击查看原文


相关推荐


【自动驾驶】自动驾驶概述 ⑤ ( 自动驾驶硬件概述 | 车载计算单元 IPC | 车辆线控系统 )
韩曙亮9/30/2025

一、车载计算单元 IPC1、车载计算单元 IPC 简介2、高性能计算3、高安全冗余4、高环境适应性二、车辆线控系统1、自动驾驶线控系统2、线控转向 ( Steer-by-Wire )3、线控制动 ( Brake-by-Wire )4、线控驱动 ( Throttle-by-Wire )5、线控换挡 ( Shift-by-Wire )6、线控悬挂 ( Suspension-by-Wire )7、线控系统举例说明


ptyhon 基础语法学习(对比php)
come1123410/2/2025

非常棒!我们已经系统地对比了 Python 和 PHP 的所有主要数据类型。分类Python 类型对应 PHP 概念关键点基础intfloatstrintfloatstringPython 字符串格式化用f-string序列listtuple索引数组list可变(用[]),tuple不可变(用()映射dict关联数组键值对集合 (用{}集合set的结果元素唯一且无序特殊boolNoneboolnullFalse和None(首字母大写)这份表格可以作为您未来的速查手册。


Excel转PDF不分页
Bella_chene2025/10/2

将Excel转成PDF后,会发现存在分页的现象,理想是希望将一整个表格按实际情况缩放显示到PDF的一页上去 操作办法:打开excel表格,ctrl+P打开打印页面,点击页面设置,选择“将工作表调整为一页” 然后在右侧就可以看到效果,点击保存就可以了


[linux仓库]深入解析Linux动态链接与动态库加载:理解背后的原理与技巧
egoist20232025/10/2

🌟 各位看官好,我是egoist2023! 🌍 Linux == Linux is not Unix ! 🚀 今天来学习Linux的指令知识,并学会灵活使用这些指令。 👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享更多人哦! 目录 进程如何看到动态库 进程间如何共享库 动态链接 编译器对可执行程序动手脚 动态库相对地址  程序如何和库具体映射 程序怎么进行库调用 全局偏移量表GOT 库间依赖(看看即可) 总结 进程如何看到动态库


DeepSeek V3.1-Terminus、阿里 Qwen3-Max、ChatGPT Pulse 同周登场!| AI Weekly 9.22-9.28
AI信息Gap2025/10/3

卷,卷起来了! 📢 本周 AI 快讯 | 1 分钟速览🚀 1️⃣ 🚀 DeepSeek 发布 V3.1-Terminus :Agent 性能提升 28%,HLE 测试跃升全球第三,仅次于 Grok 4 和 GPT-5,SimpleQA 准确率达 96.8%。 2️⃣ 💰 阿里云栖大会七连发 :3800 亿 AI 基建投资起步,万亿参数 Qwen3-Max 对标 GPT-5,AIME 25 和 HMMT 数学测试满分 100 分。 3️⃣ 🖥️ Kimi 推出 OK Compu


UNIX下C语言编程与实践16-UNIX 磁盘空间划分:引导块、超级块、i 节点区、数据区的功能解析
迎風吹頭髮2025/10/4

一、UNIX 磁盘空间划分的核心逻辑:为何分为四个区域? UNIX 文件系统在格式化时,会将磁盘分区(如 /dev/sda1)划分为引导块(Boot Block)、超级块(Super Block)、i 节点区(Inode Area)、数据区(Data Area)四个连续的区域。这种划分并非随意设计,而是为了实现“系统启动-文件系统管理-数据存储”的完整功能链路,确保磁盘空间的有序管理和高效访问。 核心定位:四个区域各司其职且相互依赖——引导块负责“启动系统”,超级块负责“管理文件系统全局信息


Nginx 配置负载均衡(详细版)
1加1等于2025/10/6

本文详细介绍关于Nginx 配置负载均衡,包括配置文件结构、多种负载均衡策略、如何修改均衡策略以及其他一些重要的配置。 本文目录 一、、Nginx 配置负载均衡1. 配置文件结构 二、Nginx 负载均衡策略1. 轮询(`默认策略`)2. 加权轮询3. IP 哈希4. 最少连接 三、修改负载均衡策略四、Nginx 负载均衡其他配置1. 健康检查2. 会话保持3. 超时设置 一、、Nginx 配置负载均衡 1. 配置文件结构 Nginx 的负载均衡配置主要


Kubernetes核心技术与集群部署项目
企鹅侠客2025/10/7

从集群搭建到核心功能应用的完整流程,内容涵盖集群部署、核心组件、资源管理、安全机制、持久化、监控与应用交付等关键技术。首先介绍 Kubernetes 的架构与特性,深入讲解 kubeadm 与二进制两种集群搭建方式,包括 etcd 集群部署、Master 与 Node 组件安装、证书签发及高可用集群实现。核心技术部分系统解析 Pod 的运行机制、调度策略、健康检查与资源限制,讲解 Deployment、StatefulSet、DaemonSet、Job 等控制器的应用场景。配置管理方面介绍 Co


前端路由的秘密:手写一个迷你路由,看懂Hash和History的较量
良山有风来2025/10/9

你是不是也遇到过这样的场景?开发单页面应用时,页面跳转后刷新一下就404,或者URL里带着难看的#号,被产品经理吐槽不够优雅? 别担心,今天我就带你彻底搞懂前端路由的两种模式,手把手教你实现一个迷你路由,并告诉你什么场景该用哪种方案。 读完本文,你能获得一套完整的前端路由知识体系,从原理到实战,再到生产环境配置,一次性全搞定! 为什么需要前端路由? 想象一下,你正在开发一个后台管理系统。传统做法是每个页面都对应一个HTML文件,切换页面就要重新加载,体验特别差。 而前端路由让你可以在一个页面内实


汽车软件开发的质量和安全管理流程
NewCarRen2025/10/10

摘要 软件开发流程是智能车辆(联网车辆和自动驾驶车辆)的核心,必须精心管理。自动化与联网功能的开发分别通过功能安全和网络安全开发流程实现,且需遵循相关标准,这些标准规定了流程、最佳实践、危害、威胁及管理策略。通过改进软件开发流程,智能车辆的人体工程学性能将得到提升。本文阐述了如何通过软件开发来管理实现自动化与联网功能的流程,以及是否可能改变管理团队的策略与软件开发流程。 1、引言 智能车辆是一种能够从周围环境中获取信息,并对信息进行处理,从而实现自主安全行驶且不造成任何伤害的车辆。此外,智

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0