AMediaCodec yuv编码成mp4文件demo
时间: 2024-02-28 20:54:23 浏览: 179
以下是使用 `AMediaCodec` 对 YUV 数据进行编码并写入 MP4 文件的示例代码:
```cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <android/native_window.h>
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaFormat.h>
#define WIDTH 640
#define HEIGHT 480
int main() {
// 初始化 AMediaFormat 对象
AMediaFormat* mediaFormat = AMediaFormat_new();
AMediaFormat_setString(mediaFormat, AMEDIAFORMAT_KEY_MIME, "video/avc");
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_WIDTH, WIDTH);
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_HEIGHT, HEIGHT);
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_BIT_RATE, WIDTH * HEIGHT * 3 * 8);
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, OMX_COLOR_FormatYUV420SemiPlanar);
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_FRAME_RATE, 30);
AMediaFormat_setInt32(mediaFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1);
// 创建 MediaCodec 编码器
AMediaCodec* mediaCodec = AMediaCodec_createEncoderByType("video/avc");
AMediaCodec_configure(mediaCodec, mediaFormat, nullptr, nullptr, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
// 打开文件并写入 MP4 文件头
int fd = open("output.mp4", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
uint8_t header[] = {0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x0d, 0xf9, 0x70, 0x50, 0x16, 0xe8, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x3c, 0x80};
write(fd, header, sizeof(header));
// 开始编码
AMediaCodec_start(mediaCodec);
ssize_t outputBufferIndex = -1;
AMediaCodecBufferInfo outputBufferInfo = {};
// 读取 YUV 数据并写入编码器
uint8_t* yuvData = (uint8_t*) malloc(WIDTH * HEIGHT * 3 / 2);
uint32_t frameNumber = 0;
while (frameNumber < 300) {
// 读取 YUV 数据
// ...
// 获取输入缓冲区
ssize_t inputBufferIndex = AMediaCodec_dequeueInputBuffer(mediaCodec, 0);
if (inputBufferIndex >= 0) {
uint8_t* inputBuffer = AMediaCodec_getInputBuffer(mediaCodec, inputBufferIndex, nullptr);
memcpy(inputBuffer, yuvData, WIDTH * HEIGHT * 3 / 2);
// 将输入缓冲区提交给编码器
AMediaCodec_queueInputBuffer(mediaCodec, inputBufferIndex, 0, WIDTH * HEIGHT * 3 / 2, 0, 0);
}
// 获取输出缓冲区
outputBufferIndex = AMediaCodec_dequeueOutputBuffer(mediaCodec, &outputBufferInfo, 0);
while (outputBufferIndex >= 0) {
uint8_t* outputBuffer = AMediaCodec_getOutputBuffer(mediaCodec, outputBufferIndex, nullptr);
// 将编码后的数据写入文件
write(fd, outputBuffer, outputBufferInfo.size);
// 释放输出缓冲区
AMediaCodec_releaseOutputBuffer(mediaCodec, outputBufferIndex, false);
// 获取下一个输出缓冲区
outputBufferIndex = AMediaCodec_dequeueOutputBuffer(mediaCodec, &outputBufferInfo, 0);
}
// 延时一段时间,模拟实时数据流
usleep(33000);
frameNumber++;
}
// 结束编码
AMediaCodec_stop(mediaCodec);
AMediaCodec_delete(mediaCodec);
AMediaFormat_delete(mediaFormat);
// 写入 MP4 文件尾
uint8_t footer[] = {0x00, 0x00, 0x00, 0x01, 0x65, 0x88, 0x81, 0x00};
write(fd, footer, sizeof(footer));
// 关闭文件
close(fd);
return 0;
}
```
在上面的示例代码中,我们首先创建了一个 `AMediaFormat` 对象,用于指定编码器的参数,包括视频宽度、高度、比特率、颜色格式、帧率和关键帧间隔等。接着,我们使用 `AMediaCodec_createEncoderByType` 函数创建了一个 `AMediaCodec` 对象,并调用 `AMediaCodec_configure` 函数进行编码器的配置。然后,我们打开文件并写入 MP4 文件头,并调用 `AMediaCodec_start` 函数开始编码。
在编码器启动后,我们通过循环读取 YUV 数据并写入编码器。对于每一帧 YUV 数据,我们首先通过调用 `AMediaCodec_dequeueInputBuffer` 函数获取一个输入缓冲区,并将 YUV 数据拷贝到该缓冲区中。然后,我们调用 `AMediaCodec_queueInputBuffer` 函数将输入缓冲区提交给编码器。接着,我们通过调用 `AMediaCodec_dequeueOutputBuffer` 函数获取一个输出缓冲区,并将编码后的数据写入文件。最后,我们调用 `AMediaCodec_releaseOutputBuffer` 函数释放输出缓冲区,并继续获取下一个输出缓冲区。
在循环结束后,我们调用 `AMediaCodec_stop` 函数结束编码器,并写入 MP4 文件尾。最后,我们关闭文件并释放相关资源。需要注意的是,为了保证编码器的稳定性,我们在循环中模拟实时数据流并延时一定的时间。
阅读全文