Release NaraMotion v0.26.0: Structure of Arrays (SoA), Path Arena Allocator, Uniform Grid Indexing, Dual-Buffer Rendering & Centroid Spring Pop-in
b516e00 verified Download c_src/blend2d_video.cpp from ArifiIslam/NaraMotion: direct link, hf CLI and curl.
- Browser
- Download file 21.2 kB
-
https://huggingface.co/ArifiIslam/NaraMotion/resolve/main/c_src/blend2d_video.cpp
- Command line
-
hf download hf://ArifiIslam/NaraMotion/c_src/blend2d_video.cpp
-
curl -L -o blend2d_video.cpp https://huggingface.co/ArifiIslam/NaraMotion/resolve/main/c_src/blend2d_video.cpp
21.2 kB
| // ============================================================================ | |
| // Exact FFmpeg C Types (Matching FFmpeg 6.x / 7.x ABI) | |
| // ============================================================================ | |
| struct AVRational { | |
| int num; | |
| int den; | |
| }; | |
| struct AVStream { | |
| int index; | |
| int id; | |
| void* codecpar; | |
| AVRational time_base; | |
| int64_t start_time; | |
| int64_t duration; | |
| int64_t nb_frames; | |
| int64_t cur_dts; | |
| void* metadata; | |
| AVRational avg_frame_rate; | |
| AVRational r_frame_rate; | |
| AVRational sample_aspect_ratio; | |
| int nb_side_data; | |
| void* side_data; | |
| }; | |
| struct AVOutputFormat { | |
| const char* name; | |
| const char* long_name; | |
| const char* mime_type; | |
| const char* extensions; | |
| int audio_codec; | |
| int video_codec; | |
| int subtitle_codec; | |
| int flags; | |
| }; | |
| struct AVFormatContext { | |
| const void* av_class; | |
| void* iformat; | |
| AVOutputFormat* oformat; | |
| void* priv_data; | |
| void* pb; | |
| int ctx_flags; | |
| unsigned int nb_streams; | |
| AVStream** streams; | |
| }; | |
| struct AVCodecContext { | |
| const void* av_class; | |
| int log_level_offset; | |
| int codec_type; | |
| const void* codec; | |
| int codec_id; | |
| unsigned int codec_tag; | |
| void* priv_data; | |
| void* internal; | |
| void* opaque; | |
| int64_t bit_rate; | |
| int bit_rate_tolerance; | |
| int global_quality; | |
| int compression_level; | |
| int flags; | |
| int flags2; | |
| uint8_t* extradata; | |
| int extradata_size; | |
| AVRational time_base; | |
| int ticks_per_frame; | |
| int delay; | |
| int width, height; | |
| int coded_width, coded_height; | |
| AVRational sample_aspect_ratio; | |
| int pix_fmt; | |
| }; | |
| struct AVFrame { | |
| uint8_t* data[8]; | |
| int linesize[8]; | |
| uint8_t** extended_data; | |
| int width, height; | |
| int nb_samples; | |
| int format; | |
| int key_frame; | |
| int pict_type; | |
| AVRational sample_aspect_ratio; | |
| int64_t pts; | |
| int64_t pkt_dts; | |
| AVRational time_base; | |
| }; | |
| struct AVPacket { | |
| void* buf; | |
| int64_t pts; | |
| int64_t dts; | |
| uint8_t* data; | |
| int size; | |
| int stream_index; | |
| int flags; | |
| void* side_data; | |
| int side_data_elems; | |
| int64_t duration; | |
| int64_t pos; | |
| void* opaque; | |
| void* opaque_ref; | |
| AVRational time_base; | |
| }; | |
| // ============================================================================ | |
| // FFmpeg Function Pointer Declarations | |
| // ============================================================================ | |
| typedef int (*fn_avformat_alloc_output_context2)(AVFormatContext** ctx, void* oformat, const char* format_name, const char* filename); | |
| typedef AVStream* (*fn_avformat_new_stream)(AVFormatContext* s, const void* c); | |
| typedef int (*fn_avformat_write_header)(AVFormatContext* s, void** options); | |
| typedef int (*fn_av_write_trailer)(AVFormatContext* s); | |
| typedef int (*fn_avio_open)(void** s, const char* url, int flags); | |
| typedef int (*fn_avio_closep)(void** s); | |
| typedef int (*fn_av_interleaved_write_frame)(AVFormatContext* s, AVPacket* pkt); | |
| typedef void (*fn_avformat_free_context)(AVFormatContext* s); | |
| typedef void* (*fn_avcodec_find_encoder_by_name)(const char* name); | |
| typedef void* (*fn_avcodec_find_encoder)(int id); | |
| typedef void* (*fn_avcodec_alloc_context3)(const void* codec); | |
| typedef void (*fn_avcodec_free_context)(void** avctx); | |
| typedef int (*fn_avcodec_open2)(void* avctx, const void* codec, void** options); | |
| typedef int (*fn_avcodec_send_frame)(void* avctx, const AVFrame* frame); | |
| typedef int (*fn_avcodec_receive_packet)(void* avctx, AVPacket* pkt); | |
| typedef int (*fn_avcodec_parameters_from_context)(void* par, const void* codec); | |
| typedef AVFrame* (*fn_av_frame_alloc)(); | |
| typedef void (*fn_av_frame_free)(AVFrame** frame); | |
| typedef int (*fn_av_frame_get_buffer)(AVFrame* frame, int align); | |
| typedef int (*fn_av_frame_make_writable)(AVFrame* frame); | |
| typedef AVPacket* (*fn_av_packet_alloc)(); | |
| typedef void (*fn_av_packet_free)(AVPacket** pkt); | |
| typedef void (*fn_av_packet_rescale_ts)(AVPacket* pkt, AVRational tb_src, AVRational tb_dst); | |
| typedef void (*fn_av_packet_unref)(AVPacket* pkt); | |
| typedef int (*fn_av_opt_set)(void* obj, const char* name, const char* val, int search_flags); | |
| typedef int (*fn_av_opt_set_int)(void* obj, const char* name, int64_t val, int search_flags); | |
| typedef int (*fn_av_opt_set_q)(void* obj, const char* name, AVRational val, int search_flags); | |
| typedef int (*fn_av_opt_set_pixel_fmt)(void* obj, const char* name, int fmt, int search_flags); | |
| typedef int (*fn_av_dict_set)(void** pm, const char* key, const char* value, int flags); | |
| typedef void (*fn_av_dict_free)(void** pm); | |
| typedef void* (*fn_sws_getContext)(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat, int flags, void* srcFilter, void* dstFilter, const double* param); | |
| typedef int (*fn_sws_scale)(void* c, const uint8_t* const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t* const dst[], const int dstStride[]); | |
| typedef void (*fn_sws_freeContext)(void* swsContext); | |
| struct FFmpegAPI { | |
| bool loaded = false; | |
| fn_avformat_alloc_output_context2 avformat_alloc_output_context2 = nullptr; | |
| fn_avformat_new_stream avformat_new_stream = nullptr; | |
| fn_avformat_write_header avformat_write_header = nullptr; | |
| fn_av_write_trailer av_write_trailer = nullptr; | |
| fn_avio_open avio_open = nullptr; | |
| fn_avio_closep avio_closep = nullptr; | |
| fn_av_interleaved_write_frame av_interleaved_write_frame = nullptr; | |
| fn_avformat_free_context avformat_free_context = nullptr; | |
| fn_avcodec_find_encoder_by_name avcodec_find_encoder_by_name = nullptr; | |
| fn_avcodec_find_encoder avcodec_find_encoder = nullptr; | |
| fn_avcodec_alloc_context3 avcodec_alloc_context3 = nullptr; | |
| fn_avcodec_free_context avcodec_free_context = nullptr; | |
| fn_avcodec_open2 avcodec_open2 = nullptr; | |
| fn_avcodec_send_frame avcodec_send_frame = nullptr; | |
| fn_avcodec_receive_packet avcodec_receive_packet = nullptr; | |
| fn_avcodec_parameters_from_context avcodec_parameters_from_context = nullptr; | |
| fn_av_frame_alloc av_frame_alloc = nullptr; | |
| fn_av_frame_free av_frame_free = nullptr; | |
| fn_av_frame_get_buffer av_frame_get_buffer = nullptr; | |
| fn_av_frame_make_writable av_frame_make_writable = nullptr; | |
| fn_av_packet_alloc av_packet_alloc = nullptr; | |
| fn_av_packet_free av_packet_free = nullptr; | |
| fn_av_packet_rescale_ts av_packet_rescale_ts = nullptr; | |
| fn_av_packet_unref av_packet_unref = nullptr; | |
| fn_av_opt_set av_opt_set = nullptr; | |
| fn_av_opt_set_int av_opt_set_int = nullptr; | |
| fn_av_opt_set_q av_opt_set_q = nullptr; | |
| fn_av_opt_set_pixel_fmt av_opt_set_pixel_fmt = nullptr; | |
| fn_av_dict_set av_dict_set = nullptr; | |
| fn_av_dict_free av_dict_free = nullptr; | |
| fn_sws_getContext sws_getContext = nullptr; | |
| fn_sws_scale sws_scale = nullptr; | |
| fn_sws_freeContext sws_freeContext = nullptr; | |
| }; | |
| static FFmpegAPI g_ff; | |
| static void* load_sym(HMODULE mod, const char* name) { | |
| return (void*)GetProcAddress(mod, name); | |
| } | |
| static void* load_sym(void* mod, const char* name) { | |
| return dlsym(mod, name); | |
| } | |
| extern "C" { | |
| BL_VIDEO_EXPORT int bl_video_init_ffmpeg( | |
| const char* avcodec_path, | |
| const char* avformat_path, | |
| const char* avutil_path, | |
| const char* swscale_path | |
| ) { | |
| if (g_ff.loaded) return 0; | |
| HMODULE h_util = LoadLibraryA(avutil_path ? avutil_path : "avutil-59.dll"); | |
| HMODULE h_codec = LoadLibraryA(avcodec_path ? avcodec_path : "avcodec-61.dll"); | |
| HMODULE h_format = LoadLibraryA(avformat_path ? avformat_path : "avformat-61.dll"); | |
| HMODULE h_sws = LoadLibraryA(swscale_path ? swscale_path : "swscale-8.dll"); | |
| void* h_util = dlopen(avutil_path ? avutil_path : "libavutil.so", RTLD_NOW | RTLD_GLOBAL); | |
| void* h_codec = dlopen(avcodec_path ? avcodec_path : "libavcodec.so", RTLD_NOW | RTLD_GLOBAL); | |
| void* h_format = dlopen(avformat_path ? avformat_path : "libavformat.so", RTLD_NOW | RTLD_GLOBAL); | |
| void* h_sws = dlopen(swscale_path ? swscale_path : "libswscale.so", RTLD_NOW | RTLD_GLOBAL); | |
| if (!h_util || !h_codec || !h_format || !h_sws) { | |
| return -1; | |
| } | |
| g_ff.avformat_alloc_output_context2 = (fn_avformat_alloc_output_context2)load_sym(h_format, "avformat_alloc_output_context2"); | |
| g_ff.avformat_new_stream = (fn_avformat_new_stream)load_sym(h_format, "avformat_new_stream"); | |
| g_ff.avformat_write_header = (fn_avformat_write_header)load_sym(h_format, "avformat_write_header"); | |
| g_ff.av_write_trailer = (fn_av_write_trailer)load_sym(h_format, "av_write_trailer"); | |
| g_ff.avio_open = (fn_avio_open)load_sym(h_format, "avio_open"); | |
| g_ff.avio_closep = (fn_avio_closep)load_sym(h_format, "avio_closep"); | |
| g_ff.av_interleaved_write_frame = (fn_av_interleaved_write_frame)load_sym(h_format, "av_interleaved_write_frame"); | |
| g_ff.avformat_free_context = (fn_avformat_free_context)load_sym(h_format, "avformat_free_context"); | |
| g_ff.avcodec_find_encoder_by_name = (fn_avcodec_find_encoder_by_name)load_sym(h_codec, "avcodec_find_encoder_by_name"); | |
| g_ff.avcodec_find_encoder = (fn_avcodec_find_encoder)load_sym(h_codec, "avcodec_find_encoder"); | |
| g_ff.avcodec_alloc_context3 = (fn_avcodec_alloc_context3)load_sym(h_codec, "avcodec_alloc_context3"); | |
| g_ff.avcodec_free_context = (fn_avcodec_free_context)load_sym(h_codec, "avcodec_free_context"); | |
| g_ff.avcodec_open2 = (fn_avcodec_open2)load_sym(h_codec, "avcodec_open2"); | |
| g_ff.avcodec_send_frame = (fn_avcodec_send_frame)load_sym(h_codec, "avcodec_send_frame"); | |
| g_ff.avcodec_receive_packet = (fn_avcodec_receive_packet)load_sym(h_codec, "avcodec_receive_packet"); | |
| g_ff.avcodec_parameters_from_context = (fn_avcodec_parameters_from_context)load_sym(h_codec, "avcodec_parameters_from_context"); | |
| g_ff.av_frame_alloc = (fn_av_frame_alloc)load_sym(h_util, "av_frame_alloc"); | |
| g_ff.av_frame_free = (fn_av_frame_free)load_sym(h_util, "av_frame_free"); | |
| g_ff.av_frame_get_buffer = (fn_av_frame_get_buffer)load_sym(h_util, "av_frame_get_buffer"); | |
| g_ff.av_frame_make_writable = (fn_av_frame_make_writable)load_sym(h_util, "av_frame_make_writable"); | |
| g_ff.av_packet_alloc = (fn_av_packet_alloc)load_sym(h_codec, "av_packet_alloc"); | |
| if (!g_ff.av_packet_alloc) g_ff.av_packet_alloc = (fn_av_packet_alloc)load_sym(h_util, "av_packet_alloc"); | |
| g_ff.av_packet_free = (fn_av_packet_free)load_sym(h_codec, "av_packet_free"); | |
| if (!g_ff.av_packet_free) g_ff.av_packet_free = (fn_av_packet_free)load_sym(h_util, "av_packet_free"); | |
| g_ff.av_packet_rescale_ts = (fn_av_packet_rescale_ts)load_sym(h_codec, "av_packet_rescale_ts"); | |
| if (!g_ff.av_packet_rescale_ts) g_ff.av_packet_rescale_ts = (fn_av_packet_rescale_ts)load_sym(h_util, "av_packet_rescale_ts"); | |
| g_ff.av_packet_unref = (fn_av_packet_unref)load_sym(h_codec, "av_packet_unref"); | |
| if (!g_ff.av_packet_unref) g_ff.av_packet_unref = (fn_av_packet_unref)load_sym(h_util, "av_packet_unref"); | |
| g_ff.av_opt_set = (fn_av_opt_set)load_sym(h_util, "av_opt_set"); | |
| g_ff.av_opt_set_int = (fn_av_opt_set_int)load_sym(h_util, "av_opt_set_int"); | |
| g_ff.av_opt_set_q = (fn_av_opt_set_q)load_sym(h_util, "av_opt_set_q"); | |
| g_ff.av_opt_set_pixel_fmt = (fn_av_opt_set_pixel_fmt)load_sym(h_util, "av_opt_set_pixel_fmt"); | |
| g_ff.av_dict_set = (fn_av_dict_set)load_sym(h_util, "av_dict_set"); | |
| g_ff.av_dict_free = (fn_av_dict_free)load_sym(h_util, "av_dict_free"); | |
| g_ff.sws_getContext = (fn_sws_getContext)load_sym(h_sws, "sws_getContext"); | |
| g_ff.sws_scale = (fn_sws_scale)load_sym(h_sws, "sws_scale"); | |
| g_ff.sws_freeContext = (fn_sws_freeContext)load_sym(h_sws, "sws_freeContext"); | |
| g_ff.loaded = (g_ff.avformat_alloc_output_context2 && g_ff.avcodec_send_frame && g_ff.sws_scale); | |
| return g_ff.loaded ? 0 : -2; | |
| } | |
| // ============================================================================ | |
| // Blend2D Native C++ Video Encoder | |
| // ============================================================================ | |
| struct BLVideoEncoder { | |
| AVFormatContext* format_ctx = nullptr; | |
| void* codec_ctx = nullptr; | |
| AVStream* stream = nullptr; | |
| void* sws_ctx = nullptr; | |
| AVFrame* frame = nullptr; | |
| AVPacket* pkt = nullptr; | |
| int width = 0; | |
| int height = 0; | |
| double fps = 30.0; | |
| int64_t pts = 0; | |
| int64_t frame_count = 0; | |
| bool is_open = false; | |
| char last_error[256] = {0}; | |
| }; | |
| BL_VIDEO_EXPORT BLVideoEncoder* bl_video_encoder_create( | |
| const char* filename, | |
| int width, | |
| int height, | |
| double fps, | |
| const char* codec_name, | |
| int crf, | |
| const char* preset, | |
| int bitrate | |
| ) { | |
| if (!g_ff.loaded || !filename || width <= 0 || height <= 0 || fps <= 0.0) { | |
| return nullptr; | |
| } | |
| auto* enc = new BLVideoEncoder(); | |
| enc->width = (width % 2 == 0) ? width : width - 1; | |
| enc->height = (height % 2 == 0) ? height : height - 1; | |
| enc->fps = fps; | |
| enc->pts = 0; | |
| enc->frame_count = 0; | |
| // 1. Allocate output format context | |
| int ret = g_ff.avformat_alloc_output_context2(&enc->format_ctx, nullptr, nullptr, filename); | |
| if (ret < 0 || !enc->format_ctx) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "Could not deduce format for: %s", filename); | |
| delete enc; | |
| return nullptr; | |
| } | |
| // 2. Find video encoder | |
| const void* codec = nullptr; | |
| if (codec_name && strlen(codec_name) > 0) { | |
| codec = g_ff.avcodec_find_encoder_by_name(codec_name); | |
| } | |
| if (!codec) { | |
| codec = g_ff.avcodec_find_encoder_by_name("libx264"); | |
| } | |
| if (!codec) { | |
| codec = g_ff.avcodec_find_encoder(enc->format_ctx->oformat->video_codec); | |
| } | |
| if (!codec) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "No suitable video encoder found"); | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| delete enc; | |
| return nullptr; | |
| } | |
| // 3. Create video stream | |
| enc->stream = g_ff.avformat_new_stream(enc->format_ctx, nullptr); | |
| if (!enc->stream) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "Failed to allocate stream"); | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| delete enc; | |
| return nullptr; | |
| } | |
| enc->stream->id = enc->format_ctx->nb_streams - 1; | |
| // 4. Allocate codec context | |
| enc->codec_ctx = g_ff.avcodec_alloc_context3(codec); | |
| if (!enc->codec_ctx) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "Failed to allocate codec context"); | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| delete enc; | |
| return nullptr; | |
| } | |
| // Set properties via AVOptions | |
| g_ff.av_opt_set_int(enc->codec_ctx, "width", enc->width, 0); | |
| g_ff.av_opt_set_int(enc->codec_ctx, "height", enc->height, 0); | |
| AVRational tb = {1, (int)(fps * 1000)}; | |
| g_ff.av_opt_set_q(enc->codec_ctx, "time_base", tb, 0); | |
| enc->stream->time_base = tb; | |
| AVRational fr = {(int)fps, 1}; | |
| g_ff.av_opt_set_q(enc->codec_ctx, "framerate", fr, 0); | |
| // Set pixel format | |
| if (g_ff.av_opt_set_pixel_fmt) { | |
| g_ff.av_opt_set_pixel_fmt(enc->codec_ctx, "pix_fmt", AV_PIX_FMT_YUV420P, 0); | |
| } else { | |
| g_ff.av_opt_set(enc->codec_ctx, "pix_fmt", "yuv420p", 0); | |
| } | |
| if (bitrate > 0) { | |
| g_ff.av_opt_set_int(enc->codec_ctx, "b", bitrate, 0); | |
| } | |
| // CRF and Preset | |
| void* opts = nullptr; | |
| if (preset && strlen(preset) > 0) { | |
| g_ff.av_dict_set(&opts, "preset", preset, 0); | |
| } else { | |
| g_ff.av_dict_set(&opts, "preset", "fast", 0); | |
| } | |
| if (crf >= 0) { | |
| char crf_str[16]; | |
| snprintf(crf_str, sizeof(crf_str), "%d", crf); | |
| g_ff.av_dict_set(&opts, "crf", crf_str, 0); | |
| } | |
| // 5. Open codec | |
| ret = g_ff.avcodec_open2(enc->codec_ctx, codec, &opts); | |
| g_ff.av_dict_free(&opts); | |
| if (ret < 0) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "Failed to open video codec"); | |
| g_ff.avcodec_free_context(&enc->codec_ctx); | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| delete enc; | |
| return nullptr; | |
| } | |
| if (enc->stream->codecpar) { | |
| g_ff.avcodec_parameters_from_context(enc->stream->codecpar, enc->codec_ctx); | |
| } | |
| // 6. Open file | |
| if (!(enc->format_ctx->oformat->flags & AVFMT_NOFILE)) { | |
| ret = g_ff.avio_open(&enc->format_ctx->pb, filename, AVIO_FLAG_WRITE); | |
| if (ret < 0) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "Could not open file: %s", filename); | |
| g_ff.avcodec_free_context(&enc->codec_ctx); | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| delete enc; | |
| return nullptr; | |
| } | |
| } | |
| // 7. Write header | |
| ret = g_ff.avformat_write_header(enc->format_ctx, nullptr); | |
| if (ret < 0) { | |
| snprintf(enc->last_error, sizeof(enc->last_error), "Failed to write header"); | |
| if (enc->format_ctx->pb) g_ff.avio_closep(&enc->format_ctx->pb); | |
| g_ff.avcodec_free_context(&enc->codec_ctx); | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| delete enc; | |
| return nullptr; | |
| } | |
| // 8. Allocate frame | |
| enc->frame = g_ff.av_frame_alloc(); | |
| enc->frame->format = AV_PIX_FMT_YUV420P; | |
| enc->frame->width = enc->width; | |
| enc->frame->height = enc->height; | |
| g_ff.av_frame_get_buffer(enc->frame, 0); | |
| // 9. Allocate SIMD SwsContext | |
| enc->sws_ctx = g_ff.sws_getContext( | |
| enc->width, enc->height, AV_PIX_FMT_BGRA, | |
| enc->width, enc->height, AV_PIX_FMT_YUV420P, | |
| SWS_FAST_BILINEAR, nullptr, nullptr, nullptr | |
| ); | |
| enc->pkt = g_ff.av_packet_alloc(); | |
| enc->is_open = true; | |
| return enc; | |
| } | |
| BL_VIDEO_EXPORT int bl_video_encoder_write_raw( | |
| BLVideoEncoder* enc, | |
| const uint8_t* bgra_data, | |
| int stride | |
| ) { | |
| if (!enc || !enc->is_open || !bgra_data) return -1; | |
| if (g_ff.av_frame_make_writable(enc->frame) < 0) { | |
| return -2; | |
| } | |
| const uint8_t* src_slices[4] = { bgra_data, nullptr, nullptr, nullptr }; | |
| int src_strides[4] = { stride, 0, 0, 0 }; | |
| g_ff.sws_scale( | |
| enc->sws_ctx, | |
| src_slices, src_strides, 0, enc->height, | |
| enc->frame->data, enc->frame->linesize | |
| ); | |
| enc->frame->pts = enc->frame_count * 1000; | |
| enc->frame_count++; | |
| int ret = g_ff.avcodec_send_frame(enc->codec_ctx, enc->frame); | |
| if (ret < 0) return ret; | |
| while (ret >= 0) { | |
| ret = g_ff.avcodec_receive_packet(enc->codec_ctx, enc->pkt); | |
| if (ret == AVERROR_EAGAIN || ret == AVERROR_EOF) { | |
| break; | |
| } else if (ret < 0) { | |
| return ret; | |
| } | |
| g_ff.av_packet_rescale_ts(enc->pkt, enc->stream->time_base, enc->stream->time_base); | |
| enc->pkt->stream_index = enc->stream->id; | |
| ret = g_ff.av_interleaved_write_frame(enc->format_ctx, enc->pkt); | |
| g_ff.av_packet_unref(enc->pkt); | |
| if (ret < 0) return ret; | |
| } | |
| return 0; | |
| } | |
| BL_VIDEO_EXPORT int bl_video_encoder_close(BLVideoEncoder* enc) { | |
| if (!enc || !enc->is_open) return 0; | |
| g_ff.avcodec_send_frame(enc->codec_ctx, nullptr); | |
| while (g_ff.avcodec_receive_packet(enc->codec_ctx, enc->pkt) == 0) { | |
| g_ff.av_packet_rescale_ts(enc->pkt, enc->stream->time_base, enc->stream->time_base); | |
| enc->pkt->stream_index = enc->stream->id; | |
| g_ff.av_interleaved_write_frame(enc->format_ctx, enc->pkt); | |
| g_ff.av_packet_unref(enc->pkt); | |
| } | |
| if (enc->format_ctx) { | |
| g_ff.av_write_trailer(enc->format_ctx); | |
| } | |
| if (enc->codec_ctx) { | |
| g_ff.avcodec_free_context(&enc->codec_ctx); | |
| } | |
| if (enc->frame) { | |
| g_ff.av_frame_free(&enc->frame); | |
| } | |
| if (enc->pkt) { | |
| g_ff.av_packet_free(&enc->pkt); | |
| } | |
| if (enc->sws_ctx) { | |
| g_ff.sws_freeContext(enc->sws_ctx); | |
| enc->sws_ctx = nullptr; | |
| } | |
| if (enc->format_ctx) { | |
| if (!(enc->format_ctx->oformat->flags & AVFMT_NOFILE) && enc->format_ctx->pb) { | |
| g_ff.avio_closep(&enc->format_ctx->pb); | |
| } | |
| g_ff.avformat_free_context(enc->format_ctx); | |
| enc->format_ctx = nullptr; | |
| } | |
| enc->is_open = false; | |
| return 0; | |
| } | |
| BL_VIDEO_EXPORT void bl_video_encoder_destroy(BLVideoEncoder* enc) { | |
| if (enc) { | |
| if (enc->is_open) { | |
| bl_video_encoder_close(enc); | |
| } | |
| delete enc; | |
| } | |
| } | |
| BL_VIDEO_EXPORT int64_t bl_video_encoder_get_frame_count(const BLVideoEncoder* enc) { | |
| return enc ? enc->frame_count : 0; | |
| } | |
| BL_VIDEO_EXPORT const char* bl_video_encoder_get_last_error(const BLVideoEncoder* enc) { | |
| return enc ? enc->last_error : "Null encoder"; | |
| } | |
| } // extern "C" | |