..
This commit is contained in:
@@ -1,47 +1,38 @@
|
||||
# CMake 최소 버전 및 프로젝트 이름 설정
|
||||
cmake_minimum_required(VERSION 3.18.1)
|
||||
project("native_renderer")
|
||||
|
||||
# --- 1. FFmpeg 빌드 스크립트 실행 ---
|
||||
# NOTE: 이 명령은 CMake 설정 단계에서 딱 한 번 실행되어 FFmpeg 라이브러리를 빌드합니다.
|
||||
# ffmpeg 폴더에 있는 build_ffmpeg.sh 스크립트를 실행합니다.
|
||||
execute_process(
|
||||
COMMAND sh ../../../../ffmpeg/build_ffmpeg.sh
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE FFMPEG_BUILD_RESULT
|
||||
)
|
||||
|
||||
if(NOT FFMPEG_BUILD_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "FFmpeg build script failed with exit code: ${FFMPEG_BUILD_RESULT}")
|
||||
endif()
|
||||
# --- 실행 끝 ---
|
||||
# --- 1. 경로 변수 설정 ---
|
||||
# NOTE: .so 파일들이 있는 lib 폴더와 include 폴더의 경로를 각각 명확히 지정합니다.
|
||||
set(JNI_LIBS_ROOT ${CMAKE_SOURCE_DIR}/../../jniLibs/${ANDROID_ABI})
|
||||
set(JNI_SO_DIR ${JNI_LIBS_ROOT}/lib)
|
||||
set(JNI_INCLUDE_DIR ${JNI_LIBS_ROOT}/include)
|
||||
|
||||
|
||||
# --- 2. 빌드된 라이브러리 참조 ---
|
||||
# NOTE: 이제 FFmpeg은 미리 빌드된 라이브러리(prebuilt)처럼 취급됩니다.
|
||||
# jniLibs 폴더 경로 설정
|
||||
set(JNI_LIBS_DIR ${CMAKE_SOURCE_DIR}/../../jniLibs/${ANDROID_ABI})
|
||||
# --- 2. 헤더 파일 경로 추가 ---
|
||||
include_directories(${JNI_INCLUDE_DIR})
|
||||
|
||||
# 헤더 파일 경로 추가
|
||||
include_directories(${JNI_LIBS_DIR}/include)
|
||||
|
||||
# 각 .so 파일을 IMPORTED 라이브러리로 추가
|
||||
# --- 3. 미리 빌드된 FFmpeg .so 라이브러리들을 IMPORTED 타겟으로 선언 ---
|
||||
# NOTE: IMPORTED_LOCATION 경로가 lib 폴더를 포함하도록 수정되었습니다.
|
||||
add_library(avformat SHARED IMPORTED)
|
||||
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/libavformat.so)
|
||||
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${JNI_SO_DIR}/libavformat.so)
|
||||
|
||||
add_library(avcodec SHARED IMPORTED)
|
||||
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/libavcodec.so)
|
||||
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${JNI_SO_DIR}/libavcodec.so)
|
||||
|
||||
add_library(avutil SHARED IMPORTED)
|
||||
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/libavutil.so)
|
||||
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${JNI_SO_DIR}/libavutil.so)
|
||||
|
||||
add_library(swscale SHARED IMPORTED)
|
||||
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/libswscale.so)
|
||||
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${JNI_SO_DIR}/libswscale.so)
|
||||
|
||||
add_library(swresample SHARED IMPORTED)
|
||||
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${JNI_LIBS_DIR}/libswresample.so)
|
||||
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${JNI_SO_DIR}/libswresample.so)
|
||||
|
||||
|
||||
# --- 3. 네이티브 렌더러 라이브러리 빌드 및 링크 ---
|
||||
# --- 4. 네이티브 렌더러 라이브러리 빌드 설정 ---
|
||||
# NOTE: 앱의 C++ 소스 파일들을 여기에 모두 나열합니다.
|
||||
add_library(native_renderer SHARED
|
||||
native_renderer.cpp
|
||||
Renderer.cpp
|
||||
@@ -49,16 +40,22 @@ add_library(native_renderer SHARED
|
||||
MediaAsset.cpp
|
||||
)
|
||||
|
||||
|
||||
# --- 5. 최종 라이브러리 링크 ---
|
||||
# NOTE: 안드로이드 기본 라이브러리와 위에서 선언한 FFmpeg 타겟들을 링크합니다.
|
||||
find_library(log-lib log)
|
||||
find_library(android-lib android)
|
||||
find_library(nativewindow-lib nativewindow)
|
||||
|
||||
target_link_libraries(native_renderer
|
||||
# FFmpeg 라이브러리 타겟들
|
||||
avformat
|
||||
avcodec
|
||||
avutil
|
||||
swscale
|
||||
swresample
|
||||
|
||||
# 안드로이드 기본 라이브러리들
|
||||
${log-lib}
|
||||
${android-lib}
|
||||
${nativewindow-lib}
|
||||
|
||||
+115
-92
@@ -1,89 +1,117 @@
|
||||
#include "MediaAsset.h"
|
||||
#include <android/log.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <unistd.h> // close(fd) 함수를 사용하기 위해 추가
|
||||
#include <algorithm>
|
||||
|
||||
// stb_image.h의 구현부를 여기에 단 한 번만 포함시킵니다.
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
#define LOG_TAG "MediaAsset"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
// NOTE: C++ JNI 단에서 파일 디스크립터(fd)의 소유권을 가지므로, 사용 후 반드시 닫아야 합니다.
|
||||
bool MediaAsset::load(int fd) {
|
||||
release(); // 새로 로드하기 전에 기존 자원 정리
|
||||
MediaAsset::MediaAsset() = default;
|
||||
|
||||
if (fd < 0) {
|
||||
LOGE("Invalid file descriptor received: %d", fd);
|
||||
MediaAsset::~MediaAsset() {
|
||||
release();
|
||||
}
|
||||
|
||||
// 파일 확장자를 확인하여 적절한 로더를 호출하는 분기 함수
|
||||
bool MediaAsset::load(const std::string& path) {
|
||||
release();
|
||||
if (path.empty()) {
|
||||
LOGE("Load failed: Path is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: FFmpeg가 파일 디스크립터를 입력으로 받도록 "pipe:[fd]" 형식의 문자열을 생성합니다.
|
||||
char path_from_fd[32];
|
||||
snprintf(path_from_fd, sizeof(path_from_fd), "pipe:%d", fd);
|
||||
size_t dotPos = path.find_last_of('.');
|
||||
if (dotPos == std::string::npos) {
|
||||
LOGE("Load failed: Could not find file extension in path: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// FFmpeg 로더 함수를 호출합니다.
|
||||
loadMediaWithFFmpeg(path_from_fd);
|
||||
std::string extension = path.substr(dotPos + 1);
|
||||
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
|
||||
|
||||
// NOTE: 파일 디스크립터 사용이 끝났으므로 여기서 닫아줍니다. (누수 방지)
|
||||
close(fd);
|
||||
const std::vector<std::string> videoExts = {"mp4", "mkv", "avi", "mov", "webm"};
|
||||
const std::vector<std::string> imageExts = {"jpg", "jpeg", "png", "bmp", "webp"};
|
||||
|
||||
return isValid();
|
||||
bool isVideo = std::find(videoExts.begin(), videoExts.end(), extension) != videoExts.end();
|
||||
bool isImage = std::find(imageExts.begin(), imageExts.end(), extension) != imageExts.end();
|
||||
|
||||
if (isImage) {
|
||||
return loadImageWithStb(path);
|
||||
} else if (isVideo) {
|
||||
return loadVideoWithFFmpeg(path);
|
||||
}
|
||||
|
||||
LOGE("Load failed: Unsupported file format for path: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: 이 함수는 이제 "pipe:[fd]" 형식의 경로를 받아 비디오와 이미지를 모두 처리합니다.
|
||||
void MediaAsset::loadMediaWithFFmpeg(const std::string& path) {
|
||||
// 1. 미디어 파일 열기
|
||||
// stb_image.h를 사용하여 이미지를 로드하는 함수
|
||||
bool MediaAsset::loadImageWithStb(const std::string& path) {
|
||||
type_ = Type::IMAGE;
|
||||
// 4 채널(RGBA)로 강제 변환하여 로드합니다.
|
||||
imageData_ = stbi_load(path.c_str(), &width_, &height_, nullptr, 4);
|
||||
if (!imageData_) {
|
||||
LOGE("Failed to load image with stb_image: %s", stbi_failure_reason());
|
||||
return false;
|
||||
}
|
||||
LOGI("Successfully loaded image with stb_image: %s (%dx%d)", path.c_str(), width_, height_);
|
||||
return true;
|
||||
}
|
||||
|
||||
// FFmpeg을 사용하여 비디오를 로드하는 함수
|
||||
bool MediaAsset::loadVideoWithFFmpeg(const std::string& path) {
|
||||
type_ = Type::VIDEO;
|
||||
|
||||
// 1. 비디오 파일 열기
|
||||
if (avformat_open_input(&fmtCtx_, path.c_str(), nullptr, nullptr) != 0) {
|
||||
LOGE("Could not open media from path: %s", path.c_str());
|
||||
return;
|
||||
LOGE("Could not open video file: %s", path.c_str());
|
||||
release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 스트림 정보 찾기
|
||||
if (avformat_find_stream_info(fmtCtx_, nullptr) < 0) {
|
||||
LOGE("Could not find stream information.");
|
||||
LOGE("Could not find stream information for %s", path.c_str());
|
||||
release();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 최적의 비디오 스트림 찾기
|
||||
const AVCodec* codec = nullptr;
|
||||
videoStreamIdx_ = av_find_best_stream(fmtCtx_, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
|
||||
if (videoStreamIdx_ < 0) {
|
||||
LOGE("Could not find a video stream in media.");
|
||||
LOGE("Could not find a video stream in %s", path.c_str());
|
||||
release();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
AVStream* stream = fmtCtx_->streams[videoStreamIdx_];
|
||||
|
||||
// 4. 미디어 타입 결정 (비디오 vs 이미지)
|
||||
if (stream->disposition & AV_DISPOSITION_ATTACHED_PIC || stream->duration <= 0) {
|
||||
type_ = Type::IMAGE;
|
||||
} else {
|
||||
type_ = Type::VIDEO;
|
||||
}
|
||||
|
||||
// 5. 코덱 컨텍스트 준비
|
||||
// 4. 코덱 컨텍스트 준비
|
||||
codecCtx_ = avcodec_alloc_context3(codec);
|
||||
if (!codecCtx_ || avcodec_parameters_to_context(codecCtx_, stream->codecpar) < 0) {
|
||||
LOGE("Failed to create codec context.");
|
||||
if (!codecCtx_ || avcodec_parameters_to_context(codecCtx_, fmtCtx_->streams[videoStreamIdx_]->codecpar) < 0) {
|
||||
LOGE("Failed to create codec context for %s", path.c_str());
|
||||
release();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. 코덱 열기
|
||||
if (avcodec_open2(codecCtx_, codec, nullptr) < 0) {
|
||||
LOGE("Could not open codec.");
|
||||
LOGE("Could not open codec for %s", path.c_str());
|
||||
release();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. 프레임 및 패킷 할당
|
||||
frame_ = av_frame_alloc();
|
||||
packet_ = av_packet_alloc();
|
||||
if (!frame_ || !packet_) {
|
||||
LOGE("Could not allocate frame or packet.");
|
||||
LOGE("Could not allocate frame or packet for %s", path.c_str());
|
||||
release();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 7. RGBA 변환을 위한 SwsContext 준비
|
||||
@@ -94,48 +122,30 @@ void MediaAsset::loadMediaWithFFmpeg(const std::string& path) {
|
||||
width_, height_, AV_PIX_FMT_RGBA,
|
||||
SWS_BILINEAR, nullptr, nullptr, nullptr
|
||||
);
|
||||
|
||||
if (!swsCtx_) {
|
||||
LOGE("Could not create SwsContext.");
|
||||
LOGE("Could not create SwsContext for %s", path.c_str());
|
||||
release();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 8. RGBA 픽셀 데이터를 담을 버퍼 할당
|
||||
rgbBuffer_.resize(width_ * height_ * 4);
|
||||
|
||||
// 9. 이미지일 경우, 첫 프레임을 미리 디코딩하여 버퍼에 저장
|
||||
if (type_ == Type::IMAGE) {
|
||||
if (av_read_frame(fmtCtx_, packet_) >= 0 && packet_->stream_index == videoStreamIdx_) {
|
||||
if (avcodec_send_packet(codecCtx_, packet_) == 0) {
|
||||
if (avcodec_receive_frame(codecCtx_, frame_) == 0) {
|
||||
uint8_t* dst[4] = { rgbBuffer_.data(), nullptr, nullptr, nullptr };
|
||||
int dstStride_arr[4] = { width_ * 4, 0, 0, 0 };
|
||||
sws_scale(swsCtx_, frame_->data, frame_->linesize, 0, height_, dst, dstStride_arr);
|
||||
imageData_ = rgbBuffer_.data(); // imageData_가 버퍼를 가리키도록 설정
|
||||
}
|
||||
}
|
||||
}
|
||||
av_packet_unref(packet_);
|
||||
// 이미지는 첫 프레임만 필요하므로 컨텍스트를 미리 닫아 자원 절약
|
||||
avformat_close_input(&fmtCtx_);
|
||||
fmtCtx_ = nullptr;
|
||||
}
|
||||
|
||||
LOGI("Successfully loaded media (Type: %s, W: %d, H: %d)",
|
||||
(type_ == Type::IMAGE ? "Image" : "Video"), width_, height_);
|
||||
}
|
||||
|
||||
|
||||
MediaAsset::~MediaAsset() {
|
||||
release();
|
||||
LOGI("Successfully loaded video: %s (W: %d, H: %d)", path.c_str(), width_, height_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MediaAsset::release() {
|
||||
imageData_ = nullptr; // rgbBuffer_가 해제될 것이므로 포인터만 초기화
|
||||
// 이미지 데이터 해제
|
||||
if (type_ == Type::IMAGE && imageData_) {
|
||||
stbi_image_free(imageData_);
|
||||
}
|
||||
imageData_ = nullptr;
|
||||
|
||||
// FFmpeg 비디오 자원 해제
|
||||
if (packet_) av_packet_free(&packet_);
|
||||
if (frame_) av_frame_free(&frame_);
|
||||
if (codecCtx_) avcodec_close(codecCtx_); // avcodec_free_context 전에 호출
|
||||
if (codecCtx_) avcodec_free_context(&codecCtx_);
|
||||
if (fmtCtx_) avformat_close_input(&fmtCtx_);
|
||||
if (swsCtx_) sws_freeContext(swsCtx_);
|
||||
@@ -146,6 +156,7 @@ void MediaAsset::release() {
|
||||
fmtCtx_ = nullptr;
|
||||
swsCtx_ = nullptr;
|
||||
|
||||
// 공통 변수 초기화
|
||||
type_ = Type::UNKNOWN;
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
@@ -155,44 +166,56 @@ void MediaAsset::release() {
|
||||
|
||||
bool MediaAsset::isValid() const {
|
||||
if (type_ == Type::IMAGE) {
|
||||
return !rgbBuffer_.empty() && width_ > 0 && height_ > 0;
|
||||
return imageData_ != nullptr && width_ > 0 && height_ > 0;
|
||||
} else if (type_ == Type::VIDEO) {
|
||||
return fmtCtx_ != nullptr && codecCtx_ != nullptr && frame_ != nullptr && swsCtx_ != nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Move Constructor and Assignment Operator ---
|
||||
// --- 이동 생성자 및 이동 할당 연산자 (자원 소유권 이전) ---
|
||||
|
||||
MediaAsset::MediaAsset(MediaAsset&& other) noexcept
|
||||
: type_(other.type_), width_(other.width_), height_(other.height_),
|
||||
imageData_(nullptr), fmtCtx_(other.fmtCtx_), codecCtx_(other.codecCtx_),
|
||||
frame_(other.frame_), packet_(other.packet_), swsCtx_(other.swsCtx_),
|
||||
videoStreamIdx_(other.videoStreamIdx_), rgbBuffer_(std::move(other.rgbBuffer_)) {
|
||||
imageData_(other.imageData_), rgbBuffer_(std::move(other.rgbBuffer_)),
|
||||
fmtCtx_(other.fmtCtx_), codecCtx_(other.codecCtx_), frame_(other.frame_),
|
||||
packet_(other.packet_), swsCtx_(other.swsCtx_), videoStreamIdx_(other.videoStreamIdx_) {
|
||||
|
||||
if (!rgbBuffer_.empty()) {
|
||||
imageData_ = rgbBuffer_.data();
|
||||
}
|
||||
other.imageData_ = nullptr; other.fmtCtx_ = nullptr; other.codecCtx_ = nullptr;
|
||||
other.frame_ = nullptr; other.packet_ = nullptr; other.swsCtx_ = nullptr;
|
||||
other.type_ = Type::UNKNOWN; other.videoStreamIdx_ = -1;
|
||||
// 소유권을 이전했으므로, 원본 객체의 포인터들은 초기화하여 이중 해제를 방지
|
||||
other.type_ = Type::UNKNOWN;
|
||||
other.imageData_ = nullptr;
|
||||
other.fmtCtx_ = nullptr;
|
||||
other.codecCtx_ = nullptr;
|
||||
other.frame_ = nullptr;
|
||||
other.packet_ = nullptr;
|
||||
other.swsCtx_ = nullptr;
|
||||
}
|
||||
|
||||
MediaAsset& MediaAsset::operator=(MediaAsset&& other) noexcept {
|
||||
if (this != &other) {
|
||||
release();
|
||||
type_ = other.type_; width_ = other.width_; height_ = other.height_;
|
||||
fmtCtx_ = other.fmtCtx_; codecCtx_ = other.codecCtx_; frame_ = other.frame_;
|
||||
packet_ = other.packet_; swsCtx_ = other.swsCtx_; videoStreamIdx_ = other.videoStreamIdx_;
|
||||
release(); // 기존 자원 정리
|
||||
|
||||
// 자원 소유권 이전
|
||||
type_ = other.type_;
|
||||
width_ = other.width_;
|
||||
height_ = other.height_;
|
||||
imageData_ = other.imageData_;
|
||||
rgbBuffer_ = std::move(other.rgbBuffer_);
|
||||
fmtCtx_ = other.fmtCtx_;
|
||||
codecCtx_ = other.codecCtx_;
|
||||
frame_ = other.frame_;
|
||||
packet_ = other.packet_;
|
||||
swsCtx_ = other.swsCtx_;
|
||||
videoStreamIdx_ = other.videoStreamIdx_;
|
||||
|
||||
if (!rgbBuffer_.empty()) {
|
||||
imageData_ = rgbBuffer_.data();
|
||||
}
|
||||
|
||||
other.imageData_ = nullptr; other.fmtCtx_ = nullptr; other.codecCtx_ = nullptr;
|
||||
other.frame_ = nullptr; other.packet_ = nullptr; other.swsCtx_ = nullptr;
|
||||
other.type_ = Type::UNKNOWN; other.videoStreamIdx_ = -1;
|
||||
// 원본 객체 초기화
|
||||
other.type_ = Type::UNKNOWN;
|
||||
other.imageData_ = nullptr;
|
||||
other.fmtCtx_ = nullptr;
|
||||
other.codecCtx_ = nullptr;
|
||||
other.frame_ = nullptr;
|
||||
other.packet_ = nullptr;
|
||||
other.swsCtx_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
|
||||
// FFmpeg 헤더 파일 포함
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
@@ -13,46 +13,64 @@ extern "C" {
|
||||
|
||||
class MediaAsset {
|
||||
public:
|
||||
// 미디어 타입을 구분하기 위한 enum
|
||||
enum class Type { UNKNOWN, IMAGE, VIDEO };
|
||||
|
||||
MediaAsset() = default;
|
||||
MediaAsset();
|
||||
~MediaAsset();
|
||||
|
||||
// 복사를 방지하고 이동만 허용 (효율적인 자원 관리)
|
||||
MediaAsset(const MediaAsset&) = delete;
|
||||
MediaAsset& operator=(const MediaAsset&) = delete;
|
||||
MediaAsset(MediaAsset&& other) noexcept;
|
||||
MediaAsset& operator=(MediaAsset&& other) noexcept;
|
||||
bool load(int fd); // 수정
|
||||
// bool load(const std::string& path);
|
||||
|
||||
// 파일 경로를 받아 이미지 또는 비디오를 로드하는 메인 함수
|
||||
bool load(const std::string& path);
|
||||
// 모든 자원을 해제하는 함수
|
||||
void release();
|
||||
|
||||
// 현재 MediaAsset이 유효한 데이터를 가지고 있는지 확인
|
||||
bool isValid() const;
|
||||
|
||||
// Getter 함수들
|
||||
Type getType() const { return type_; }
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
|
||||
// 이미지 데이터 포인터를 반환 (이미지 타입일 때만 유효)
|
||||
uint8_t* getImageData() const { return imageData_; }
|
||||
|
||||
// 비디오 프레임의 RGB 데이터 버퍼를 반환 (비디오 타입일 때만 유효)
|
||||
std::vector<uint8_t>& getRgbBuffer() { return rgbBuffer_; }
|
||||
|
||||
// 비디오 처리에 필요한 FFmpeg 컨텍스트들을 반환하는 Getter 함수들
|
||||
AVFormatContext* getFormatContext() const { return fmtCtx_; }
|
||||
AVCodecContext* getCodecContext() const { return codecCtx_; }
|
||||
AVFrame* getFrame() const { return frame_; }
|
||||
AVPacket* getPacket() const { return packet_; }
|
||||
SwsContext* getSwsContext() const { return swsCtx_; }
|
||||
int getVideoStreamIndex() const { return videoStreamIdx_; }
|
||||
std::vector<uint8_t>& getRgbBuffer() { return rgbBuffer_; }
|
||||
|
||||
private:
|
||||
void loadVideo(const std::string& path);
|
||||
void loadImage(const std::string& path);
|
||||
void loadMediaWithFFmpeg(const std::string& path);
|
||||
// 내부 헬퍼 함수
|
||||
bool loadVideoWithFFmpeg(const std::string& path);
|
||||
bool loadImageWithStb(const std::string& path);
|
||||
|
||||
// 공통 멤버 변수
|
||||
Type type_ = Type::UNKNOWN;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
|
||||
// 이미지 전용 멤버 변수
|
||||
uint8_t* imageData_ = nullptr;
|
||||
|
||||
// 비디오 전용 멤버 변수 (FFmpeg)
|
||||
std::vector<uint8_t> rgbBuffer_;
|
||||
AVFormatContext* fmtCtx_ = nullptr;
|
||||
AVCodecContext* codecCtx_ = nullptr;
|
||||
AVFrame* frame_ = nullptr;
|
||||
AVPacket* packet_ = nullptr;
|
||||
SwsContext* swsCtx_ = nullptr;
|
||||
int videoStreamIdx_ = -1;
|
||||
std::vector<uint8_t> rgbBuffer_;
|
||||
};
|
||||
Reference in New Issue
Block a user