AMediaCodec解码后的数据保存成yuv文件


AMediaCodecBufferInfo info;
ssize_t index = AMediaCodec_dequeueOutputBuffer(ylMediaDecode->getAMediaCodec(), &info, 3000);   
if (index >= 0) {
   uint8_t *buffer = AMediaCodec_getOutputBuffer(ylMediaDecode->getAMediaCodec(), index, &outsize);
   auto filename = "/storage/emulated/0/Android/data/com.xxx.xxx/files/test.yuv"
   FILE fp_out = fopen(name.c_str(),"w+");
   fwrite(buffer, 1, info.size,fp_out);
} 

我使用android ndk AMediaCodec解码h265/h264数据,并尝试将获得的解码数据保存到test.yuv文件中。 但yuv文件无法用7yuv软件打开。 如何正确将解码后的数据保存为 yuv I420 文件?

参考:

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答由chatgpt基于相关博客总结生成:

    我可以回答关于IT方面的问题,但是你没有提出具体的问题。请提供具体的问题,我会尽力给出解决方案。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

AMediaCodecBufferInfo info;
ssize_t index = AMediaCodec_dequeueOutputBuffer(ylMediaDecode->getAMediaCodec(), &info, 3000);   
if (index >= 0) {
   uint8_t *buffer = AMediaCodec_getOutputBuffer(ylMediaDecode->getAMediaCodec(), index, &outsize);
   auto filename = "/storage/emulated/0/Android/data/com.xxx.xxx/files/test.yuv";
   FILE* fp_out = fopen(name.c_str(), "w"); // 使用文本模式打开文件
   fwrite(buffer, 1, info.size, fp_out);
   fclose(fp_out); // 关闭文件
} 

找一下转换工具类

Android中使用MediaCodec硬件解码,高效率得到YUV格式帧,快速保存JPEG图片
可以参考下,非常详细


Android mp4提取视频流并解码为YUV_mp4解码nv21_原总破局的博客-CSDN博客 我们首先要明白NV21的存储样式NV21 YYYYYYYYVUVU => YUV420SP(ANDROID)数据先存Y,再VU交替存储提取yuv数据要用到下面的API如果我们想获取YUV的数据那么就要用到MediaFormat int yuvFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible; if (!isSupportYuvFormat(yuvFormat, code_mp4解码nv21 https://blog.csdn.net/qq_15255121/article/details/119458430

可以参考:
MediaCodec解码视频,得到YUV值,一帧一帧加载到SD卡中保存:https://blog.csdn.net/m0_60259116/article/details/126559530
或者这个:
MediaCodec 使用-- YUV打包成MP4:https://zhuanlan.zhihu.com/p/565416124?utm_id=0

用MediaCodec,可以解码

转换成mp4

每一次解答都是一次用心理解的过程,期望对你有所帮助。
参考结合AI智能库,如有帮助,恭请采纳。

以下是一个示例代码片段,演示了如何将解码数据保存为YUV I420格式的文件:

#include <iostream>  
#include <fstream>  
  
// 假设你已经获得了解码数据存储在buffer中,宽度和高度分别为width和height  
void saveYUVToFile(const uint8_t* buffer, size_t size, int width, int height, const std::string& filename) {  
    FILE* fp_out = fopen(filename.c_str(), "wb");  
    if (!fp_out) {  
        std::cerr << "Failed to open file: " << filename << std::endl;  
        return;  
    }  
  
    // 写入YUV图像的宽度和高度  
    int64_t offset = 0;  
    fwrite(&offset, sizeof(int64_t), 1, fp_out);  
    offset += sizeof(int64_t) * 3;  // 偏移量  
  
    // 写入Y分量数据  
    int ySize = width * height;  
    uint8_t* yData = new uint8_t[ySize];  
    memcpy(yData, buffer, ySize);  
  
    // 写入U分量数据  
    int uSize = width * height / 4;  
    uint8_t* uData = new uint8_t[uSize];  
    // TODO: 从buffer中提取U分量数据,并存储到uData中  
  
    // 写入V分量数据  
    int vSize = width * height / 4;  
    uint8_t* vData = new uint8_t[vSize];  
    // TODO: 从buffer中提取V分量数据,并存储到vData中  
  
    // 将YUV数据写入文件  
    fseek(fp_out, offset, SEEK_SET);  
    fwrite(yData, sizeof(uint8_t), ySize, fp_out);  
    offset += ySize;  
    fwrite(uData, sizeof(uint8_t), uSize, fp_out);  
    offset += uSize;  
    fwrite(vData, sizeof(uint8_t), vSize, fp_out);  
  
    fclose(fp_out);  
    delete[] yData;  
    delete[] uData;  
    delete[] vData;  
}

#注释部分需要根据你的实际情况进行修改

使用 AMediaCodec 进行视频解码后,可以将输出的像素数据保存为 YUV 文件。

YUV 是一种常见的视频格式,它由 Y、U、V 三个分量构成。Y 表示亮度(明亮度),而 U、V 表示色度(色差)。其中,Y 分量的取值范围是 0255,而 U、V 分量的取值范围是 -128127。在保存为 YUV 文件时,通常需要先将每个分量的数据存储在不同的文件中,然后按照顺序将它们串联起来形成完整的 YUV 数据流。

下面是一个简单的示例代码,它演示了如何使用 AMediaCodec 将视频解码为 YUV 数据并保存到文件中:

// 创建解码器
MediaFormat format = MediaFormat.createVideoFormat("video/mp4v-es", 640, 480);
AMediaCodec codec = AMediaCodec.createDecoderByType("video/mp4v-es");
codec.configure(format, surface, null, 0);
codec.start();

// 循环解码视频帧
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
boolean isEOS = false;
int frameCount = 0;
while (!Thread.interrupted()) {
    if (!isEOS) {
        int inputBufferIndex = codec.dequeueInputBuffer(-1);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
            int sampleSize = extractor.readSampleData(inputBuffer, 0);
            if (sampleSize < 0) {
                codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                isEOS = true;
            } else {
                codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                extractor.advance();
            }
        }
    }

    int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
    switch (outputBufferIndex) {
        case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
            outputBuffers = codec.getOutputBuffers();
            break;
        case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
            MediaFormat newFormat = codec.getOutputFormat();
            break;
        case MediaCodec.INFO_TRY_AGAIN_LATER:
            break;
        default:
            ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
            byte[] yuvData = new byte[640 * 480 * 3 / 2];
            outputBuffer.get(yuvData);
            saveYUVDataToFile(yuvData, frameCount);
            frameCount++;
            codec.releaseOutputBuffer(outputBufferIndex, true);
            break;
    }

    if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
        break;
    }
}

// 释放资源
codec.stop();
codec.release();
extractor.release();

在代码中,我们使用了 ByteBuffer 类型的数组来保存输入和输出的数据缓冲区,使用 MediaCodec.BufferInfo 类型的对象来保存输出缓冲区的相关信息。在每次解码完成后,我们将 YUV 数据保存到文件中。这里我们假设每一帧的数据大小为 640×480×3/2 字节(即一个 YUV 数据流中每 1 个像素占 1.5 个字节),并将它们依次写入文件中。

需要注意的是,由于 AMediaCodec 可能使用硬件加速来进行视频解码,因此在调用 releaseOutputBuffer 方法时需要指定第二个参数为 true,以便让解码器正确地处理输出缓冲区。另外,在保存 YUV 数据时,我们也需要指定正确的格式和顺序,以便后续的处理程序能够正确地读取和使用它们。

综上所述,使用 AMediaCodec 解码后的 YUV 数据可以通过简单的文件读写操作进行保存和后续处理。

AMediaCodec是Android提供的一种用于视频编解码的API,可以用来将视频文件进行解码处理后输出,其中输出的数据格式一般为YUV,即将RGB格式的像素数据转化为YUV格式的像素数据。因此,在使用AMediaCodec进行视频解码后,我们需要将输出的YUV格式数据保存下来以便后续使用。

以下是如何将AMediaCodec解码后的数据保存成YUV文件的步骤:

  1. 创建一个文件指针并打开一个文件,用于存储解码后的视频数据。
FILE *pFile = fopen(fileName, "wb"); // 打开文件
  1. 从AMediaCodec中获取解码后的视频数据,使用如下代码片段:
AMediaCodecBufferInfo info;
size_t outIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, timeoutUs);
if (outIndex >= 0) {
    // 获取输出缓冲区
    AMediaCodecBuffer* outBuffer = AMediaCodec_getOutputBuffer(codec, outIndex);
    // 写入数据到文件
    fwrite(outBuffer->data, 1, info.size, pFile);
    // 释放缓冲区
    AMediaCodec_releaseOutputBuffer(codec, outIndex, false);
}

在上述代码中,我们首先从AMediaCodec中获取解码后的输出数据缓冲区,并获取到该缓冲区的大小信息。然后,我们使用fwrite函数将该缓冲区中的数据写入到指定的文件中。最后,我们通过AMediaCodec_releaseOutputBuffer函数释放该缓冲区。

注意:由于YUV格式的像素数据中每个像素占据多个字节,所以我们在写入数据时应该将每个像素的数据单独处理,具体操作方法如下:

  1. 将YUV格式的像素数据转化为RGBA格式的像素数据

因为YUV格式的像素数据不能直接进行图像处理,一般需要将其转化为RGBA格式的像素数据后再进行处理。可以使用如下代码将YUV像素数据转化为RGBA像素数据:

void yuvToRgb(unsigned char *yuvData, unsigned char *rgbData, int width, int height) {
    for (int i = 0, j = 0; i < width * height * 3; i += 6, j += 4) {
        int y0 = yuvData[j];
        int u = yuvData[width * height + j / 2] - 128;
        int y1 = yuvData[j + 1];
        int v = yuvData[width * height + j / 2 + 1] - 128;

        rgbData[i] = y0 + 1.13983f * v;
        rgbData[i + 1] = y0 - 0.39465f * u - 0.58060f * v;
        rgbData[i + 2] = y0 + 2.03211f * u;

        rgbData[i + 3] = y1 + 1.13983f * v;
        rgbData[i + 4] = y1 - 0.39465f * u - 0.58060f * v;
        rgbData[i + 5] = y1 + 2.03211f * u;
    }
}

在上述代码中,我们首先定义了两个数组变量分别用于存储YUV格式和RGBA格式的像素数据。然后,我们通过循环遍历每个像素,将其YUV数据转化为RGBA数据并存储到指定的数组中。

  1. 将RGBA格式的像素数据写入到指定文件中

将RGBA格式的像素数据写入到文件中的代码与上面类似,只是在写入时需要将每个像素的数据单独处理。具体操作方法如下:

fwrite(rgbData, 1, width*height*3, pFile);

在上述代码中,我们首先定义了一个数组变量用于存储RGBA格式的像素数据。然后,我们使用fwrite函数将该数组中的数据单独写入到指定的文件中。

  1. 关闭文件指针

当我们将需要保存的数据全部写入到文件中后,需要使用fclose函数关闭文件指针,以确保文件保存成功。

fclose(pFile);

总的来说,将AMediaCodec解码后的数据保存成YUV文件只需要遵循如上所述的五个步骤即可。需要注意的是,在实际操作中,我们需要根据具体情况灵活运用上面的代码以达到最佳效果。