xuning

录音实现

  1 +// Tencent is pleased to support the open source community by making ncnn available.
  2 +//
  3 +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved.
  4 +//
  5 +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  6 +// in compliance with the License. You may obtain a copy of the License at
  7 +//
  8 +// https://opensource.org/licenses/BSD-3-Clause
  9 +//
  10 +// Unless required by applicable law or agreed to in writing, software distributed
  11 +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  12 +// CONDITIONS OF ANY KIND, either express or implied. See the License for the
  13 +// specific language governing permissions and limitations under the License.
  14 +
  15 +#include "ndkcamera.h"
  16 +#include <android/log.h>
  17 +#include <chrono>
  18 +#include <unistd.h>
  19 +
  20 +#define LOG_TAG "NdkCameraRecording"
  21 +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  22 +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
  23 +
  24 +bool NdkCameraWindow::startRecording(const char* filepath) {
  25 + std::lock_guard<std::mutex> lock(recording_mutex);
  26 +
  27 + if (recording_active) {
  28 + LOGE("Recording already active");
  29 + return false;
  30 + }
  31 +
  32 + // 初始化录制状态
  33 + video_encoder = nullptr;
  34 + audio_encoder = nullptr;
  35 + media_muxer = nullptr;
  36 + recording_active = true;
  37 + video_track_index = -1;
  38 + audio_track_index = -1;
  39 + muxer_started = false;
  40 +
  41 + // 创建录制线程
  42 + recording_thread = new std::thread(&NdkCameraWindow::recording_worker, this);
  43 +
  44 + LOGI("Recording started: %s", filepath);
  45 + return true;
  46 +}
  47 +
  48 +bool NdkCameraWindow::stopRecording() {
  49 + {
  50 + std::lock_guard<std::mutex> lock(recording_mutex);
  51 + if (!recording_active) {
  52 + LOGE("Recording not active");
  53 + return false;
  54 + }
  55 + recording_active = false;
  56 + }
  57 +
  58 + // 等待录制线程结束
  59 + if (recording_thread && recording_thread->joinable()) {
  60 + recording_thread->join();
  61 + delete recording_thread;
  62 + recording_thread = nullptr;
  63 + }
  64 +
  65 + // 清理资源
  66 + if (media_muxer) {
  67 + AMediaMuxer_stop(media_muxer);
  68 + AMediaMuxer_delete(media_muxer);
  69 + media_muxer = nullptr;
  70 + }
  71 +
  72 + if (video_encoder) {
  73 + AMediaCodec_stop(video_encoder);
  74 + AMediaCodec_delete(video_encoder);
  75 + video_encoder = nullptr;
  76 + }
  77 +
  78 + if (audio_encoder) {
  79 + AMediaCodec_stop(audio_encoder);
  80 + AMediaCodec_delete(audio_encoder);
  81 + audio_encoder = nullptr;
  82 + }
  83 +
  84 + LOGI("Recording stopped");
  85 + return true;
  86 +}
  87 +
  88 +bool NdkCameraWindow::isRecording() const {
  89 + std::lock_guard<std::mutex> lock(recording_mutex);
  90 + return recording_active;
  91 +}
  92 +
  93 +void NdkCameraWindow::recording_worker() {
  94 + // 这里实现录制工作线程
  95 + // 由于MediaCodec实现较复杂,这里先提供一个框架
  96 + // 实际实现需要处理视频编码、音频编码和混合器
  97 +
  98 + while (recording_active) {
  99 + // 处理视频帧队列
  100 + std::vector<uint8_t> frame;
  101 + {
  102 + std::lock_guard<std::mutex> lock(recording_mutex);
  103 + if (!video_frame_queue.empty()) {
  104 + frame = video_frame_queue.front();
  105 + video_frame_queue.pop();
  106 + }
  107 + }
  108 +
  109 + if (!frame.empty()) {
  110 + // 处理视频帧编码
  111 + // 这里需要实现MediaCodec编码逻辑
  112 + }
  113 +
  114 + // 短暂休眠避免过度占用CPU
  115 + usleep(1000); // 1ms
  116 + }
  117 +}
  118 +
  119 +bool NdkCameraWindow::setup_video_encoder(int width, int height, int fps) {
  120 + // 创建视频编码器
  121 + const char* mime_type = "video/avc";
  122 + video_encoder = AMediaCodec_createEncoderByType(mime_type);
  123 + if (!video_encoder) {
  124 + LOGE("Failed to create video encoder");
  125 + return false;
  126 + }
  127 +
  128 + // 配置视频格式
  129 + AMediaFormat* format = AMediaFormat_new();
  130 + AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime_type);
  131 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, width);
  132 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height);
  133 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, fps);
  134 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1);
  135 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 2000000);
  136 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT,
  137 + AMEDIACODEC_COLOR_FormatYUV420Flexible);
  138 +
  139 + // 配置编码器
  140 + media_status_t status = AMediaCodec_configure(video_encoder, format,
  141 + nullptr, nullptr,
  142 + AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
  143 + AMediaFormat_delete(format);
  144 +
  145 + if (status != AMEDIA_OK) {
  146 + LOGE("Failed to configure video encoder: %d", status);
  147 + AMediaCodec_delete(video_encoder);
  148 + video_encoder = nullptr;
  149 + return false;
  150 + }
  151 +
  152 + // 启动编码器
  153 + status = AMediaCodec_start(video_encoder);
  154 + if (status != AMEDIA_OK) {
  155 + LOGE("Failed to start video encoder: %d", status);
  156 + AMediaCodec_delete(video_encoder);
  157 + video_encoder = nullptr;
  158 + return false;
  159 + }
  160 +
  161 + LOGI("Video encoder setup: %dx%d %dfps", width, height, fps);
  162 + return true;
  163 +}
  164 +
  165 +bool NdkCameraWindow::setup_audio_encoder() {
  166 + // 创建音频编码器
  167 + const char* mime_type = "audio/mp4a-latm";
  168 + audio_encoder = AMediaCodec_createEncoderByType(mime_type);
  169 + if (!audio_encoder) {
  170 + LOGE("Failed to create audio encoder");
  171 + return false;
  172 + }
  173 +
  174 + // 配置音频格式
  175 + AMediaFormat* format = AMediaFormat_new();
  176 + AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mime_type);
  177 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, 44100);
  178 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, 1);
  179 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 128000);
  180 + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_AAC_PROFILE, 2); // AAC LC
  181 +
  182 + // 配置编码器
  183 + media_status_t status = AMediaCodec_configure(audio_encoder, format,
  184 + nullptr, nullptr,
  185 + AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
  186 + AMediaFormat_delete(format);
  187 +
  188 + if (status != AMEDIA_OK) {
  189 + LOGE("Failed to configure audio encoder: %d", status);
  190 + AMediaCodec_delete(audio_encoder);
  191 + audio_encoder = nullptr;
  192 + return false;
  193 + }
  194 +
  195 + // 启动编码器
  196 + status = AMediaCodec_start(audio_encoder);
  197 + if (status != AMEDIA_OK) {
  198 + LOGE("Failed to start audio encoder: %d", status);
  199 + AMediaCodec_delete(audio_encoder);
  200 + audio_encoder = nullptr;
  201 + return false;
  202 + }
  203 +
  204 + LOGI("Audio encoder setup");
  205 + return true;
  206 +}
  207 +
  208 +void NdkCameraWindow::encode_video_frame(const unsigned char* nv21_data, int width, int height, int64_t timestamp_us) {
  209 + if (!video_encoder || !recording_active) {
  210 + return;
  211 + }
  212 +
  213 + // 将视频帧添加到队列供录制线程处理
  214 + std::vector<uint8_t> frame_data(nv21_data, nv21_data + width * height * 3 / 2);
  215 + std::lock_guard<std::mutex> lock(recording_mutex);
  216 + video_frame_queue.push(std::move(frame_data));
  217 +}