...
This commit is contained in:
@@ -203,6 +203,13 @@ bool MediaAsset::loadVideoWithFFmpeg(int fd) {
|
||||
LOGE("Could not find a video stream");
|
||||
release(); return false;
|
||||
}
|
||||
AVStream* stream = fmtCtx_->streams[videoStreamIdx_];
|
||||
if (stream->avg_frame_rate.den != 0) {
|
||||
fps_ = av_q2d(stream->avg_frame_rate);
|
||||
} else {
|
||||
fps_ = 30.0; // FPS 정보가 없는 경우 기본값 30
|
||||
}
|
||||
LOGI("Video FPS detected: %f", fps_);
|
||||
codecCtx_ = avcodec_alloc_context3(codec);
|
||||
if (!codecCtx_ || avcodec_parameters_to_context(codecCtx_, fmtCtx_->streams[videoStreamIdx_]->codecpar) < 0) {
|
||||
LOGE("Failed to create codec context");
|
||||
@@ -244,6 +251,14 @@ bool MediaAsset::loadVideoWithFFmpeg(const std::string& path) {
|
||||
const AVCodec* codec = nullptr;
|
||||
videoStreamIdx_ = av_find_best_stream(fmtCtx_, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
|
||||
if (videoStreamIdx_ < 0) { release(); return false; }
|
||||
// --- ⬇️ FPS 추출 코드 추가 (동일한 코드) ⬇️ ---
|
||||
AVStream* stream = fmtCtx_->streams[videoStreamIdx_];
|
||||
if (stream->avg_frame_rate.den != 0) {
|
||||
fps_ = av_q2d(stream->avg_frame_rate);
|
||||
} else {
|
||||
fps_ = 30.0;
|
||||
}
|
||||
LOGI("Video FPS detected: %f", fps_);
|
||||
codecCtx_ = avcodec_alloc_context3(codec);
|
||||
if (!codecCtx_ || avcodec_parameters_to_context(codecCtx_, fmtCtx_->streams[videoStreamIdx_]->codecpar) < 0) { release(); return false; }
|
||||
if (avcodec_open2(codecCtx_, codec, nullptr) < 0) { release(); return false; }
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
AVPacket* getPacket() const { return packet_; }
|
||||
SwsContext* getSwsContext() const { return swsCtx_; }
|
||||
int getVideoStreamIndex() const { return videoStreamIdx_; }
|
||||
double getFps() const { return fps_; } // <-- FPS getter 추가
|
||||
|
||||
private:
|
||||
bool loadInternal(const std::string& path);
|
||||
@@ -72,4 +73,5 @@ private:
|
||||
AVPacket* packet_ = nullptr;
|
||||
SwsContext* swsCtx_ = nullptr;
|
||||
int videoStreamIdx_ = -1;
|
||||
double fps_ = 0.0; // <-- FPS 저장 변수 추가
|
||||
};
|
||||
@@ -22,7 +22,10 @@ Renderer::Renderer() {
|
||||
setAnimationMode(static_cast<int>(AnimationMode::PAN));
|
||||
setTransitionMode(static_cast<int>(TransitionMode::FADE));
|
||||
}
|
||||
Renderer::~Renderer() { release(); }
|
||||
Renderer::~Renderer() {
|
||||
stopRenderLoop();
|
||||
release();
|
||||
}
|
||||
void Renderer::release() { std::lock_guard<std::mutex> lock(renderMutex_); currentMedia_.release(); nextMedia_.release(); }
|
||||
void Renderer::setNextMedia(int fd) { preloader_.startNextPreload(fd); }
|
||||
void Renderer::setAnimationSpeed(float speed) { animationSpeed_ = speed > 0 ? speed : 1.0f; }
|
||||
@@ -37,6 +40,48 @@ void Renderer::setTransitionMode(int mode) {
|
||||
configuredTransitionMode_ = (mode >= 0 && mode <= static_cast<int>(TransitionMode::MOSAIC)) ? static_cast<TransitionMode>(mode) : TransitionMode::FADE;
|
||||
}
|
||||
|
||||
void Renderer::startRenderLoop(ANativeWindow* window) {
|
||||
if (isRunning_) return; // 이미 실행 중이면 무시
|
||||
|
||||
nativeWindow_ = window;
|
||||
if (nativeWindow_) {
|
||||
ANativeWindow_acquire(nativeWindow_);
|
||||
isRunning_ = true;
|
||||
renderThread_ = std::thread([this]() {
|
||||
LOGI("C++ Render thread started.");
|
||||
while (isRunning_) {
|
||||
auto frameStartTime = std::chrono::steady_clock::now();
|
||||
|
||||
renderFrame(nativeWindow_); // 기존의 복잡한 renderFrame 함수를 그대로 호출
|
||||
|
||||
auto frameEndTime = std::chrono::steady_clock::now();
|
||||
auto frameDuration = std::chrono::duration_cast<std::chrono::milliseconds>(frameEndTime - frameStartTime);
|
||||
|
||||
// --- ⬇️ 수정: 고정 딜레이 -> 가변 딜레이 ⬇️ ---
|
||||
// 현재 설정된 목표 딜레이(currentFrameDelay_)에 맞춰 대기
|
||||
auto sleepTime = currentFrameDelay_ - frameDuration;
|
||||
if (sleepTime > std::chrono::milliseconds(0)) {
|
||||
std::this_thread::sleep_for(sleepTime);
|
||||
}
|
||||
}
|
||||
LOGI("C++ Render thread finished.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::stopRenderLoop() {
|
||||
if (!isRunning_) return;
|
||||
|
||||
isRunning_ = false;
|
||||
if (renderThread_.joinable()) {
|
||||
renderThread_.join();
|
||||
}
|
||||
if (nativeWindow_) {
|
||||
ANativeWindow_release(nativeWindow_);
|
||||
nativeWindow_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::determineActiveAnimationMode() {
|
||||
AnimationMode modeToSetActive;
|
||||
if (configuredAnimationMode_ == AnimationMode::RANDOM) {
|
||||
@@ -202,6 +247,14 @@ void Renderer::renderFrame(ANativeWindow* window) {
|
||||
if (!currentMedia_.isValid() && preloader_.isPreloadedDataReady()) {
|
||||
currentMedia_ = preloader_.swapAndRelease();
|
||||
if (currentMedia_.isValid()) {
|
||||
// --- ⬇️ 프레임 딜레이 설정 로직 추가 ⬇️ ---
|
||||
if (currentMedia_.getType() == MediaAsset::Type::VIDEO && currentMedia_.getFps() > 0) {
|
||||
currentFrameDelay_ = std::chrono::milliseconds(static_cast<long long>(1000.0 / currentMedia_.getFps()));
|
||||
LOGI("Media type: Video. Frame delay set to %lldms for %.2f FPS", currentFrameDelay_.count(), currentMedia_.getFps());
|
||||
} else {
|
||||
currentFrameDelay_ = std::chrono::milliseconds(33); // 이미지일 경우 30fps
|
||||
LOGI("Media type: Image. Frame delay set to 33ms (~30 FPS)");
|
||||
}
|
||||
currentState_ = RenderState::ANIMATING;
|
||||
determineActiveAnimationMode();
|
||||
if(animationStrategy_) animationStrategy_->reset();
|
||||
|
||||
@@ -31,6 +31,9 @@ public:
|
||||
void release();
|
||||
std::string getDebugInfo() const;
|
||||
|
||||
void startRenderLoop(ANativeWindow* window);
|
||||
void stopRenderLoop();
|
||||
|
||||
void setAnimationSpeed(float speed);
|
||||
void setAnimationMode(int mode);
|
||||
void setFadeDuration(int durationMs);
|
||||
@@ -44,6 +47,11 @@ public:
|
||||
float& outScale, float& outOffsetX, float& outOffsetY) const;
|
||||
|
||||
private:
|
||||
|
||||
std::thread renderThread_;
|
||||
std::atomic<bool> isRunning_{false};
|
||||
ANativeWindow* nativeWindow_ = nullptr;
|
||||
|
||||
enum class RenderState { ANIMATING, TRANSITIONING };
|
||||
RenderState currentState_ = RenderState::ANIMATING;
|
||||
|
||||
@@ -66,6 +74,8 @@ private:
|
||||
long long fadeDurationMs_ = 3000;
|
||||
long long pageTurnDelayMs_ = 5000;
|
||||
float animationSpeed_ = 1.0f;
|
||||
// --- ⬇️ 목표 프레임 딜레이 변수 추가 ⬇️ ---
|
||||
std::chrono::milliseconds currentFrameDelay_{33}; // 기본값 33ms (약 30fps)
|
||||
|
||||
AnimationState getStartStateForMode(AnimationMode mode, int surfaceWidth, int surfaceHeight);
|
||||
AnimationMode predictNextAnimationMode();
|
||||
|
||||
@@ -74,6 +74,30 @@ Java_bums_lunatic_launcher_wall_NativeRenderer_nativeDestroy(JNIEnv* env, jobjec
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeStopRenderLoop(JNIEnv* env, jobject, jlong nativeHandle) {
|
||||
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
||||
if (renderer) {
|
||||
renderer->stopRenderLoop();
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeStartRenderLoop(JNIEnv* env, jobject, jlong nativeHandle, jobject surface) {
|
||||
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
||||
if (!renderer || !surface) return;
|
||||
|
||||
ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
|
||||
if (!window) {
|
||||
LOGE("Could not get ANativeWindow from Surface.");
|
||||
return;
|
||||
}
|
||||
renderer->startRenderLoop(window);
|
||||
ANativeWindow_release(window);
|
||||
}
|
||||
|
||||
|
||||
// [수정] 모든 함수가 nativeHandle을 첫 파라미터로 받도록 변경
|
||||
JNIEXPORT void JNICALL
|
||||
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeRender(JNIEnv* env, jobject, jlong nativeHandle, jobject surface) {
|
||||
|
||||
Reference in New Issue
Block a user