【音视频开发(三)】---YUV转RGB 图像颜色空间转换

Android的MediaCodec解码后,默认输出是原始视频图像格式,一般为YUV420,例如NV12, NV21。则我们需要进行YUV转RGB,以便后续输入至算法库。而如果使用循环单纯用CPU转换是比较慢的,这里使用Opencv来进行转换,Opencv虽然也是用了CPU,但是他内部做了并行计算的优化是比较快的,例如NEON/SSE等。

头文件:


#include "Flow.h"
#include <thread>
#include "DmSyncQueue.h"
#include "InputData.h"

#include <atomic>

using namespace  std;

class Yuv2RgbCv : public Flow {

public:
    Yuv2RgbCv();
    ~Yuv2RgbCv();
    void Init(int width, int height, int buffNumber=10);
    void PushData(const unsigned char *data, int size, int copy, int flag=0);
    void ReleaseOuputData(); //当外部使用完缓冲工区时释放
    void UnInit();
private:
    void Loop();

    int imageW;
    int imageH;

    int mMaxBufferNumber;
    int mBufferHead;
    int mBufferTail;
    atomic<int> mBufferSize;
    unsigned char **mOutputBuffer;
};

初始化:

void Yuv2RgbCv::Init(int width, int height, int buffNumber)
{
    run = true;
    imageW = width;
    imageH = height;
    mMaxBufferNumber = buffNumber;

    mInputDataList = new DmSyncQueue<InputData_t>(mMaxBufferNumber);
    mOutputDataList = new DmSyncQueue<InputData_t>(mMaxBufferNumber);

  
    mOutputBuffer =  new unsigned char* [mMaxBufferNumber+1];
    for (int i = 0; i < mMaxBufferNumber+1; i++) {
        mOutputBuffer[i] = new unsigned char [imageW * imageH * 3];
    }

    mBufferHead = 0;
    mBufferTail = 0;
    mBufferSize.store(mMaxBufferNumber);
    mLoopThread = new std::thread(&Yuv2RgbCv::Loop, this);
   
}
void Yuv2RgbCv::UnInit()
{
    run = false;
    if(mLoopThread) mLoopThread->join();

    for (int i = 0; i < mMaxBufferNumber+1; i++) {
        delete [] mOutputBuffer[i] ;
    }
    delete [] mOutputBuffer ;

    SAFE_DELETE(mInputDataList);
    SAFE_DELETE(mOutputDataList);
}

数据的输入或者释放:

void Yuv2RgbCv::PushData(const unsigned char *data, int size, int copy, int flag)
{
    InputData_t input;
    if (size <= 0 ) return;
    if (copy) {
        unsigned char *newdata = new unsigned char[size];
        memcpy(newdata, data, size);
        data = newdata;
    }
    input.dataPtr = data;
    input.size = size;
    input.flag = flag;
    input.needRealeaseMemory = copy;
    mInputDataList->Put(input);
}

void Yuv2RgbCv::ReleaseOuputData()
{
    mBufferTail = (mBufferTail + 1) % mMaxBufferNumber;
    mBufferSize++;
}

数据转码工作线程:

void Yuv2RgbCv::Loop()
{
    unsigned char *buffer ;
    while(run) {
        if(mBufferSize.load() == 0) {
            usleep(1000);
            continue;
        }
        InputData_t input;
        if(mInputStreamDataList == NULL) {
            if(!mInputDataList->Take(input)) continue;
        } else {
            if(!mInputStreamDataList->Take(input)) continue;
        }
        buffer = mOutputBuffer[mBufferHead];
        //DM_NATIVE_ERR_PRINT("-------- Yuv2RgbCv size= %p %d %p--------\n", input.dataPtr, input.size, buffer);

        //转码
        cv::Mat rgbImg(imageH, imageW,CV_8UC3, (void *)buffer);
       // rgbImg.data = buffer;
        cv::Mat yuvImg(imageH*1.5, imageW, CV_8UC1,  (void *)input.dataPtr);
       // yuvImg.data = yuvImg.data;

        mBufferHead = (mBufferHead + 1) % mMaxBufferNumber;
        mBufferSize--;

        //memcpy(yuvImg.data, *iter, decoder.mYUVSize);
        cv::cvtColor(yuvImg, rgbImg, CV_YUV2RGB_NV12);

        InputData_t output;
        output.dataPtr = buffer;
        output.size = imageH * imageW * 3;
        output.flag = input.flag;
        output.needRealeaseMemory = false;

        mOutputDataList->Put(output);
        CALLBACK_InputDataFinished(input);
        if (input.needRealeaseMemory) {
            SAFE_DELETE_ARRAY(input.dataPtr);
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
将 mp4 视频格式转换RGB 图像,需要经过以下几个步骤: 1. 使用 `MediaExtractor` 解析视频文件,获取视频中每个音视频轨道的信息。 2. 使用 `MediaCodec` 解码视频轨道中的数据,得到原始的 YUV 数据。 3. 将 YUV 数据转换RGB 数据。 4. 将 RGB 数据渲染到屏幕上。 下面是一个简单的示例代码,可以将 mp4 视频格式转换RGB 图像并显示在屏幕上: ```java private void decodeVideo(String videoPath) { try { // 创建 MediaExtractor 并指定要解析的视频文件路径 MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource(videoPath); // 获取视频中的视频轨道 MediaFormat format = null; int videoTrackIndex = -1; for (int i = 0; i < extractor.getTrackCount(); i++) { format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if (mime.startsWith("video/")) { videoTrackIndex = i; break; } } if (videoTrackIndex == -1) { // 没有找到视频轨道 return; } // 创建 MediaCodec 并配置为解码器 extractor.selectTrack(videoTrackIndex); MediaCodec codec = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME)); codec.configure(format, null, null, 0); codec.start(); // 获取视频帧的 YUV 数据 ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); boolean isEOS = false; long presentationTimeUs = 0; while (!Thread.interrupted()) { if (!isEOS) { int inIndex = codec.dequeueInputBuffer(10000); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; int sampleSize = extractor.readSampleData(buffer, 0); if (sampleSize < 0) { // 已经读到了视频末尾 codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); isEOS = true; } else { presentationTimeUs = extractor.getSampleTime(); codec.queueInputBuffer(inIndex, 0, sampleSize, presentationTimeUs, 0); extractor.advance(); } } } int outIndex = codec.dequeueOutputBuffer(info, 10000); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: outputBuffers = codec.getOutputBuffers(); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: // 获取解码后的视频格式 format = codec.getOutputFormat(); break; case MediaCodec.INFO_TRY_AGAIN_LATER: break; default: ByteBuffer buffer = outputBuffers[outIndex]; // 将 YUV 数据转换RGB 数据 byte[] yuvData = new byte[info.size]; buffer.get(yuvData); byte[] rgbData = convertYUVtoRGB(yuvData, format.getInteger(MediaFormat.KEY_WIDTH), format.getInteger(MediaFormat.KEY_HEIGHT)); // 渲染 RGB 数据到屏幕上 renderRGB(rgbData, format.getInteger(MediaFormat.KEY_WIDTH), format.getInteger(MediaFormat.KEY_HEIGHT)); codec.releaseOutputBuffer(outIndex, true); break; } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { // 已经处理完所有的视频帧 break; } } codec.stop(); codec.release(); extractor.release(); } catch (IOException e) { e.printStackTrace(); } } private byte[] convertYUVtoRGB(byte[] yuvData, int width, int height) { // TODO: 将 YUV 数据转换RGB 数据 return null; } private void renderRGB(byte[] rgbData, int width, int height) { // TODO: 渲染 RGB 数据到屏幕上 } ``` 需要注意的是,YUV 数据和 RGB 数据的转换算法比较复杂,具体实现可以参考一些开源库,如 FFmpeg、libyuv 等。另外,在渲染 RGB 数据到屏幕上时,可以使用 OpenGL ES 来实现,具体的 OpenGL ES 代码可以参考其他资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值