MediaCodec通过C++将MP4解码成yuv例子
时间: 2024-04-06 18:28:26 浏览: 252
以下是一个简单的示例,展示了如何使用MediaCodec API将MP4文件解码为YUV格式:
```c++
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <android/native_window.h>
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaExtractor.h>
#include <media/NdkMediaFormat.h>
#define OUTPUT_YUV_FILE "/sdcard/output.yuv"
#define MAX_BUF_SIZE 0x400000
int main(int argc, char* argv[]) {
AMediaExtractor *extractor = NULL;
AMediaCodec *codec = NULL;
AMediaFormat *inputFormat = NULL, *outputFormat = NULL;
ANativeWindow* window = NULL;
uint8_t *inputBuf = NULL, *outputBuf = NULL;
ssize_t bufSize, sampleSize;
size_t inputIdx, outputIdx, outputOffset, outputSize;
uint32_t flags;
int fd, ret, i;
off_t offset;
const char* mime;
void* codecBuf = NULL;
// 打开MP4文件
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
printf("open %s failed, ret=%d\n", argv[1], fd);
return -1;
}
// 使用MediaExtractor API读取视频信息
extractor = AMediaExtractor_new();
ret = AMediaExtractor_setDataSourceFd(extractor, fd, 0, MAX_BUF_SIZE);
if (ret != AMEDIA_OK) {
printf("setDataSource failed, ret=%d\n", ret);
goto error;
}
// 获取视频的格式信息
inputFormat = AMediaExtractor_getTrackFormat(extractor, 0);
mime = AMediaFormat_toString(inputFormat);
printf("input format : %s\n", mime);
// 创建MediaCodec解码器
codec = AMediaCodec_createDecoderByType(mime);
if (codec == NULL) {
printf("createDecoderByType failed\n");
goto error;
}
// 配置解码器
ret = AMediaCodec_configure(codec, inputFormat, NULL, NULL, 0);
if (ret != AMEDIA_OK) {
printf("configure failed, ret=%d\n", ret);
goto error;
}
// 启动解码器
ret = AMediaCodec_start(codec);
if (ret != AMEDIA_OK) {
printf("start failed, ret=%d\n", ret);
goto error;
}
// 获取解码器的输出格式信息
outputFormat = AMediaCodec_getOutputFormat(codec);
mime = AMediaFormat_toString(outputFormat);
printf("output format : %s\n", mime);
// 打开输出文件
int outFd = open(OUTPUT_YUV_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0666);
if (outFd < 0) {
printf("open %s failed, ret=%d\n", OUTPUT_YUV_FILE, outFd);
goto error;
}
// 分配解码器输入和输出缓冲区
bufSize = AMediaFormat_getInt32(inputFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE);
inputBuf = (uint8_t*)malloc(bufSize);
outputBuf = (uint8_t*)malloc(bufSize * 4);
codecBuf = AMediaCodec_getInputBuffer(codec, 0, &bufSize);
// 循环读取视频帧并解码
inputIdx = outputIdx = 0;
flags = 0;
while (true) {
// 读取视频帧
sampleSize = AMediaExtractor_readSampleData(extractor, inputBuf, bufSize);
if (sampleSize < 0) {
printf("read sample data failed, ret=%d\n", sampleSize);
break;
}
// 将视频帧送入解码器
inputIdx = AMediaCodec_dequeueInputBuffer(codec, 10000);
if (inputIdx >= 0) {
memcpy(codecBuf, inputBuf, sampleSize);
AMediaCodec_queueInputBuffer(codec, inputIdx, 0, sampleSize, 0, flags);
AMediaExtractor_advance(extractor);
}
// 获取解码器的输出数据
outputIdx = AMediaCodec_dequeueOutputBuffer(codec, NULL, 10000);
if (outputIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
printf("output format changed\n");
outputFormat = AMediaCodec_getOutputFormat(codec);
mime = AMediaFormat_toString(outputFormat);
printf("output format : %s\n", mime);
} else if (outputIdx >= 0) {
// 获取输出数据的偏移量和大小
AMediaCodecBufferInfo bufferInfo;
AMediaCodec_getOutputBufferInfo(codec, &bufferInfo, outputIdx);
outputOffset = bufferInfo.offset;
outputSize = bufferInfo.size;
// 读取输出数据
ret = AMediaCodec_getOutputBufferData(codec, outputIdx, &outputBuf[outputOffset], outputSize);
if (ret != AMEDIA_OK) {
printf("getOutputBufferData failed, ret=%d\n", ret);
break;
}
// 将YUV数据写入文件
write(outFd, outputBuf, outputSize);
// 释放输出缓冲区
AMediaCodec_releaseOutputBuffer(codec, outputIdx, false);
} else if (outputIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
// do nothing
} else if (outputIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
// do nothing
} else {
printf("dequeueOutputBuffer failed, ret=%d\n", outputIdx);
break;
}
}
// 停止解码器
AMediaCodec_stop(codec);
// 释放资源
free(inputBuf);
free(outputBuf);
close(outFd);
AMediaCodec_delete(codec);
AMediaExtractor_delete(extractor);
close(fd);
return 0;
error:
if (inputBuf) free(inputBuf);
if (outputBuf) free(outputBuf);
if (codec) AMediaCodec_delete(codec);
if (extractor) AMediaExtractor_delete(extractor);
if (fd >= 0) close(fd);
if (outFd >= 0) close(outFd);
return -1;
}
```
这个示例使用`AMediaExtractor` API从MP4文件中读取视频信息,然后使用`AMediaCodec` API进行解码。在循环中,它读取一帧视频,将其送入解码器,然后从解码器中获取输出YUV数据,将其写入文件中。最后,它停止解码器并释放所有资源。请注意,这个示例仅仅是一个简单的示例,你可能需要根据自己的需求修改它。
阅读全文