正在显示
1 个修改的文件
包含
217 行增加
和
0 行删除
app/src/main/jni/ndkcamera_recording.cpp
0 → 100644
| 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 | +} |
-
请 注册 或 登录 后发表评论