|
|
|
|
@ -41,6 +41,8 @@ int video_transcode_is_active(void) { return vc_active_get(); }
|
|
|
|
|
|
|
|
|
|
/* ─── сборка без ffmpeg: стабы ─── */ |
|
|
|
|
|
|
|
|
|
void video_ffmpeg_init(void) {} |
|
|
|
|
|
|
|
|
|
int video_probe(const char* path) { (void)path; return 0; } |
|
|
|
|
int video_get_info(const char* path, struct video_info* out) { (void)path; (void)out; return -1; } |
|
|
|
|
int video_needs_transcode(const struct video_info* info) { (void)info; return 1; } |
|
|
|
|
@ -64,25 +66,36 @@ int video_extract_thumbnail(const char* src, const char* jpg_out, int max_w) {
|
|
|
|
|
|
|
|
|
|
static pthread_once_t g_ffmpeg_once = PTHREAD_ONCE_INIT; |
|
|
|
|
|
|
|
|
|
/* Перехват логов FFmpeg: все сообщения идут в наш debug-лог с категорией video,
|
|
|
|
|
* иначе ошибки ffmpeg теряются в stderr и не видно причину сбоя транскода. */ |
|
|
|
|
/* AMF передаёт свой внутренний трейс через AV_LOG_DEBUG. Остальные подробные логи FFmpeg отсекаем. */ |
|
|
|
|
static void video_av_log_cb(void* avcl, int level, const char* fmt, va_list vl) { |
|
|
|
|
(void)avcl; |
|
|
|
|
if (level > AV_LOG_WARNING) return; /* отсекаем INFO/VERBOSE/DEBUG/TRACE — демуксер спамит */ |
|
|
|
|
const char *encoder = NULL; |
|
|
|
|
if (level > AV_LOG_WARNING) { |
|
|
|
|
if (!debug_should_output(DEBUG_LEVEL_TRACE, DEBUG_CATEGORY_DEBUG)) return; |
|
|
|
|
if (!avcl || *(const AVClass **)avcl != avcodec_get_class()) return; |
|
|
|
|
const AVCodecContext *codec = avcl; |
|
|
|
|
if (!codec->codec || !codec->codec->name) return; |
|
|
|
|
const char *name = codec->codec->name; |
|
|
|
|
size_t length = strlen(name); |
|
|
|
|
if (length < 4 || strcmp(name + length - 4, "_amf")) return; |
|
|
|
|
encoder = name; |
|
|
|
|
} |
|
|
|
|
char buf[1024]; |
|
|
|
|
vsnprintf(buf, sizeof(buf), fmt, vl); |
|
|
|
|
size_t len = strlen(buf); |
|
|
|
|
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) buf[--len] = '\0'; |
|
|
|
|
if (level <= AV_LOG_ERROR) DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "ffmpeg: %s", buf); |
|
|
|
|
if (encoder) DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "ffmpeg AMF encoder=%s: %s", encoder, buf); |
|
|
|
|
else if (level <= AV_LOG_ERROR) DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "ffmpeg: %s", buf); |
|
|
|
|
else DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "ffmpeg: %s", buf); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void ffmpeg_init_once(void) { |
|
|
|
|
av_log_set_callback(video_av_log_cb); |
|
|
|
|
av_log_set_level(AV_LOG_WARNING); |
|
|
|
|
av_log_set_level(AV_LOG_DEBUG); |
|
|
|
|
avformat_network_init(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void video_ffmpeg_init(void) { pthread_once(&g_ffmpeg_once, ffmpeg_init_once); } |
|
|
|
|
|
|
|
|
|
/* строковое описание кода ошибки ffmpeg (используется только в worker-потоке) */ |
|
|
|
|
static const char* vc_errstr(int err) { |
|
|
|
|
static char buf[64]; |
|
|
|
|
@ -94,7 +107,7 @@ static const char* vc_errstr(int err) {
|
|
|
|
|
|
|
|
|
|
int video_probe(const char* path) { |
|
|
|
|
if (!path || !path[0]) return 0; |
|
|
|
|
pthread_once(&g_ffmpeg_once, ffmpeg_init_once); |
|
|
|
|
video_ffmpeg_init(); |
|
|
|
|
|
|
|
|
|
AVFormatContext* fmt = NULL; |
|
|
|
|
if (avformat_open_input(&fmt, path, NULL, NULL) != 0) return 0; |
|
|
|
|
@ -117,7 +130,7 @@ int video_get_info(const char* path, struct video_info* out) {
|
|
|
|
|
if (!out) return -1; |
|
|
|
|
memset(out, 0, sizeof(*out)); |
|
|
|
|
if (!path || !path[0]) return -1; |
|
|
|
|
pthread_once(&g_ffmpeg_once, ffmpeg_init_once); |
|
|
|
|
video_ffmpeg_init(); |
|
|
|
|
|
|
|
|
|
AVFormatContext* fmt = NULL; |
|
|
|
|
if (avformat_open_input(&fmt, path, NULL, NULL) != 0) { |
|
|
|
|
@ -445,7 +458,7 @@ static void vc_flush(struct vc_ctx* ctx, struct vc_stream* st) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int vc_run_transcode(struct vc_ctx* ctx) { |
|
|
|
|
pthread_once(&g_ffmpeg_once, ffmpeg_init_once); |
|
|
|
|
video_ffmpeg_init(); |
|
|
|
|
|
|
|
|
|
int ret = avformat_open_input(&ctx->ifmt, ctx->src, NULL, NULL); |
|
|
|
|
if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "video: avformat_open_input failed: %s", vc_errstr(ret)); return -1; } |
|
|
|
|
@ -607,7 +620,7 @@ int video_transcode_start(struct media_async* owner, struct UASYNC* ua, const ch
|
|
|
|
|
int video_extract_thumbnail(const char* src, const char* jpg_out, int max_w) { |
|
|
|
|
if (!src || !src[0] || !jpg_out || !jpg_out[0]) return -1; |
|
|
|
|
if (max_w <= 0) max_w = 320; |
|
|
|
|
pthread_once(&g_ffmpeg_once, ffmpeg_init_once); |
|
|
|
|
video_ffmpeg_init(); |
|
|
|
|
|
|
|
|
|
AVFormatContext* fmt = NULL; |
|
|
|
|
if (avformat_open_input(&fmt, src, NULL, NULL) != 0) { |
|
|
|
|
|