Android关于H.264的软编和硬编

本文详细分析了Android平台上H.264编码的软编和硬编实现。软编部分涉及编码器的初始化、数据处理、YUV422转换,以及使用ffmpeg进行编码。硬编部分则介绍了如何结合MediaCodec进行编码,并处理关键帧的pps、sps信息。整个过程涵盖了编码器配置、数据转换、线程交互等多个关键步骤。
摘要由CSDN通过智能技术生成

第六章的最后一篇分析了,之前分析摄像头预览画面的流程https://blog.csdn.net/a568478312/article/details/80725180。这里主要就是分析编码方面的核心代码。

软编

创建编码器,并开启编码和下载线程。

我们在得到相机纹理,并且经过一系列的处理之后,绘制到屏幕上。并且将最终处理后的纹理id以及EGLContext传入编码器,这样通过共享EGLContext我们就可以跨线程使用同一个纹理ID了。接下来就是初始化原始数据的队列,开启一个编码线程以及一个读取数据的线程。

void SoftEncoderAdapter::createEncoder(EGLCore* eglCore, int inputTexId) {
    this->loadTextureContext = eglCore->getContext();
    this->texId = inputTexId;
    startTime = -1;
    videoFramePool = YUY2VideoFramePool::GetInstance();
    videoFramePool->initYUY2PacketQueue();
    pthread_create(&x264EncoderThread, NULL, startEncodeThread, this);
    _msg = MSG_WINDOW_SET;
    pthread_create(&imageDownloadThread, NULL, startDownloadThread, this);
}

接下来在图像处理完成之后会绘制到屏幕上,最后进行编码

void MVRecordingPreviewController::renderFrame() {
    if (NULL != eglCore && !isInSwitchingCamera) {
//      long startTimeMills = getCurrentTime();
        if (startTime == -1) {
            startTime = getCurrentTime();
        }
        float position = ((float) (getCurrentTime() - startTime)) / 1000.0f;
        this->processVideoFrame(position);
        if (previewSurface != EGL_NO_SURFACE) {
            this->draw();
        }
//          LOGI("process Frame waste TimeMills 【%d】", (int)(getCurrentTime() - startTimeMills));
        if (isEncoding) {
            encoder->encode();
        }
    }
}

这里计算帧数,然后通知到的是下载线程进行处理,也就是读取像素并存入队列中。这个处理完之前会一直等待。

void SoftEncoderAdapter::encode(){
    while (_msg == MSG_WINDOW_SET || NULL == eglCore) {
        usleep(100 * 1000);
    }
    if (startTime == -1)
        startTime = getCurrentTime();
    int64_t curTime = getCurrentTime() - startTime;
    // need drop frames
    int expectedFrameCount = (int)(curTime/1000.0f*frameRate+0.5f);
    if (expectedFrameCount < encodedFrameCount) {
        LOGI("expectedFrameCount is %d while encodedFrameCount is %d", expectedFrameCount, encodedFrameCount);
        return;
    }
    encodedFrameCount++;
    pthread_mutex_lock(&previewThreadLock);
    pthread_mutex_lock(&mLock);
    pthread_cond_signal(&mCondition);
    pthread_mutex_unlock(&mLock);
    pthread_cond_wait(&previewThreadCondition, &previewThreadLock);
    pthread_mutex_unlock(&previewThreadLock);
}

接下来看下载线程的处理,同样是一个OpenGL 的绘制流程处理。

void SoftEncoderAdapter::renderLoop() {
    bool renderingEnabled = true;
    while (renderingEnabled) {
        pthread_mutex_lock(&mLock);
        switch (_msg) {
        case MSG_WINDOW_SET:
            LOGI("receive msg MSG_WINDOW_SET");
            initialize();
            break;
        case MSG_RENDER_LOOP_EXIT:
            LOGI("receive msg MSG_RENDER_LOOP_EXIT");
            renderingEnabled = false;
            destroy();
            break;
        default:
            break;
        }
        _msg = MSG_NONE;
        if (NULL != eglCore) {
            eglCore->makeCurrent(copyTexSurface);
            this->loadTexture();
            pthread_cond_wait(&mCondition, &mLock);
        }
        pthread_mutex_unlock(&mLock);
    }
    return;
}

初始化这里的OpenGL环境流程,这里是创建的离屏buffer,毕竟我们也不需要画到屏幕上了。

bool SoftEncoderAdapter::initialize() {
    pixelSize = videoWidth * videoHeight * PIXEL_BYTE_SIZE;
    hostGPUCopier = new HostGPUCopier();
    eglCore = new EGLCore();
    eglCore->init(loadTextureContext);
    copyTexSurface = eglCore->createOffscreenSurface(videoWidth, videoHeight);
    eglCore->makeCurrent(copyTexSurface);
    renderer = new VideoGLSurfaceRender();
    renderer->init(videoWidth, videoHeight);
    glGenFramebuffers(1, &mFBO);
    //初始化outputTexId
    glGenTextures(1, &outputTexId);
    glBindTexture(GL_TEXTURE_2D, outputTexId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, videoWidth, videoHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
    return true;
}

接下来我们看下载线程的绘制读取逻辑,为了减少glReadPixel的时间,在GPU里将数据转为YUV422,然后再进行读取,这样读取时间会减少一半。

void SoftEncoderAdapter::loadTexture() {
    if(-1 == startTime){
        return;
    }
    //1:拷贝纹理到我们的临时纹理
    int recordingDuration = getCurrentTime() - startTime;
    glViewport(0, 0, videoWidth, videoHeight);
    glBindFramebuffer(GL_FRAM
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值