xuning

录音实现

// 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));
}
\ No newline at end of file
... ...