ndkcamera_recording.cpp 7.1 KB
// Tencent is pleased to support the open source community by making ncnn available.
//
// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#include "ndkcamera.h"
#include <android/log.h>
#include <chrono>
#include <unistd.h>

#define LOG_TAG "NdkCameraRecording"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

bool NdkCameraWindow::startRecording(const char* filepath) {
    std::lock_guard<std::mutex> lock(recording_mutex);
    
    if (recording_active) {
        LOGE("Recording already active");
        return false;
    }
    
    // 初始化录制状态
    video_encoder = nullptr;
    audio_encoder = nullptr;
    media_muxer = nullptr;
    recording_active = true;
    video_track_index = -1;
    audio_track_index = -1;
    muxer_started = false;
    
    // 创建录制线程
    recording_thread = new std::thread(&NdkCameraWindow::recording_worker, this);
    
    LOGI("Recording started: %s", filepath);
    return true;
}

bool NdkCameraWindow::stopRecording() {
    {
        std::lock_guard<std::mutex> lock(recording_mutex);
        if (!recording_active) {
            LOGE("Recording not active");
            return false;
        }
        recording_active = false;
    }
    
    // 等待录制线程结束
    if (recording_thread && recording_thread->joinable()) {
        recording_thread->join();
        delete recording_thread;
        recording_thread = nullptr;
    }
    
    // 清理资源
    if (media_muxer) {
        AMediaMuxer_stop(media_muxer);
        AMediaMuxer_delete(media_muxer);
        media_muxer = nullptr;
    }
    
    if (video_encoder) {
        AMediaCodec_stop(video_encoder);
        AMediaCodec_delete(video_encoder);
        video_encoder = nullptr;
    }
    
    if (audio_encoder) {
        AMediaCodec_stop(audio_encoder);
        AMediaCodec_delete(audio_encoder);
        audio_encoder = nullptr;
    }
    
    LOGI("Recording stopped");
    return true;
}

bool NdkCameraWindow::isRecording() const {
    std::lock_guard<std::mutex> lock(recording_mutex);
    return recording_active;
}

void NdkCameraWindow::recording_worker() {
    // 这里实现录制工作线程
    // 由于MediaCodec实现较复杂,这里先提供一个框架
    // 实际实现需要处理视频编码、音频编码和混合器
    
    while (recording_active) {
        // 处理视频帧队列
        std::vector<uint8_t> frame;
        {
            std::lock_guard<std::mutex> lock(recording_mutex);
            if (!video_frame_queue.empty()) {
                frame = video_frame_queue.front();
                video_frame_queue.pop();
            }
        }
        
        if (!frame.empty()) {
            // 处理视频帧编码
            // 这里需要实现MediaCodec编码逻辑
        }
        
        // 短暂休眠避免过度占用CPU
        usleep(1000); // 1ms
    }
}

bool NdkCameraWindow::setup_video_encoder(int width, int height, int fps) {
    // 创建视频编码器
    const char* mime_type = "video/avc";
    video_encoder = AMediaCodec_createEncoderByType(mime_type);
    if (!video_encoder) {
        LOGE("Failed to create video encoder");
        return false;
    }
    
    // 配置视频格式
    AMediaFormat* format = AMediaFormat_new();
    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime_type);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, width);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, fps);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 2000000);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, 
                         AMEDIACODEC_COLOR_FormatYUV420Flexible);
    
    // 配置编码器
    media_status_t status = AMediaCodec_configure(video_encoder, format, 
                                                nullptr, nullptr, 
                                                AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
    AMediaFormat_delete(format);
    
    if (status != AMEDIA_OK) {
        LOGE("Failed to configure video encoder: %d", status);
        AMediaCodec_delete(video_encoder);
        video_encoder = nullptr;
        return false;
    }
    
    // 启动编码器
    status = AMediaCodec_start(video_encoder);
    if (status != AMEDIA_OK) {
        LOGE("Failed to start video encoder: %d", status);
        AMediaCodec_delete(video_encoder);
        video_encoder = nullptr;
        return false;
    }
    
    LOGI("Video encoder setup: %dx%d %dfps", width, height, fps);
    return true;
}

bool NdkCameraWindow::setup_audio_encoder() {
    // 创建音频编码器
    const char* mime_type = "audio/mp4a-latm";
    audio_encoder = AMediaCodec_createEncoderByType(mime_type);
    if (!audio_encoder) {
        LOGE("Failed to create audio encoder");
        return false;
    }
    
    // 配置音频格式
    AMediaFormat* format = AMediaFormat_new();
    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime_type);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, 44100);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, 1);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 128000);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_AAC_PROFILE, 2); // AAC LC
    
    // 配置编码器
    media_status_t status = AMediaCodec_configure(audio_encoder, format, 
                                                nullptr, nullptr, 
                                                AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
    AMediaFormat_delete(format);
    
    if (status != AMEDIA_OK) {
        LOGE("Failed to configure audio encoder: %d", status);
        AMediaCodec_delete(audio_encoder);
        audio_encoder = nullptr;
        return false;
    }
    
    // 启动编码器
    status = AMediaCodec_start(audio_encoder);
    if (status != AMEDIA_OK) {
        LOGE("Failed to start audio encoder: %d", status);
        AMediaCodec_delete(audio_encoder);
        audio_encoder = nullptr;
        return false;
    }
    
    LOGI("Audio encoder setup");
    return true;
}

void NdkCameraWindow::encode_video_frame(const unsigned char* nv21_data, int width, int height, int64_t timestamp_us) {
    if (!video_encoder || !recording_active) {
        return;
    }
    
    // 将视频帧添加到队列供录制线程处理
    std::vector<uint8_t> frame_data(nv21_data, nv21_data + width * height * 3 / 2);
    std::lock_guard<std::mutex> lock(recording_mutex);
    video_frame_queue.push(std::move(frame_data));
}