diff options
author | Nicolas Dato <nicolas.dato@gmail.com> | 2025-07-05 20:15:13 -0300 |
---|---|---|
committer | Nicolas Dato <nicolas.dato@gmail.com> | 2025-07-05 20:15:13 -0300 |
commit | 9cd9aba0935cc87b70fdd456bac5ba452b50d9bf (patch) | |
tree | d9cec57d684fac9bc0f46513842aa2b4ebb94458 /example | |
parent | f3c68627b3ebd72590573390733ebaacbcb18009 (diff) | |
download | libtuberia-9cd9aba0935cc87b70fdd456bac5ba452b50d9bf.tar.gz |
ading example
Diffstat (limited to 'example')
-rw-r--r-- | example/decode_filter_encode.c | 280 |
1 files changed, 243 insertions, 37 deletions
diff --git a/example/decode_filter_encode.c b/example/decode_filter_encode.c index 48f31a7..d436317 100644 --- a/example/decode_filter_encode.c +++ b/example/decode_filter_encode.c @@ -36,16 +36,12 @@ static void close_input(AVFormatContext **ctx) } static int open_input(AVFormatContext **avformatin, AVCodecContext **avcodecin, - const char *file) + int *video_idx, const char *file) { AVFormatContext *ctx = NULL; AVCodec *codec = NULL; int i = 0; - if (avformatin == NULL || avcodecin == NULL || file == NULL || !file[0]) { - return -1; - } - printf("Opening input file %s\n", file); if (avformat_open_input(&ctx, file, NULL, NULL)) { return -1; @@ -58,6 +54,7 @@ static int open_input(AVFormatContext **avformatin, AVCodecContext **avcodecin, for (i = 0; i < (int)ctx->nb_streams; i++) { if (ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + *video_idx = i; codec = avcodec_find_decoder(ctx->streams[i]->codecpar->codec_id); if (codec == NULL) { fprintf(stderr, "Couldn't find decoder for codec id %d (%s)\n", @@ -113,10 +110,6 @@ static int open_output(AVFormatContext **avformatout, const AVCodecParameters *codecparin = NULL; AVStream *stream = NULL; - if (avformatout == NULL || avcodecout == NULL || input == NULL - || file == NULL || !file[0]) { - return -1; - } printf("Opening output file %s\n", file); if (avformat_alloc_output_context2(&ctx, NULL, NULL, file) < 0 || ctx == NULL) { @@ -194,30 +187,22 @@ static int open_output(AVFormatContext **avformatout, return 0; } -static void do_tuberia(AVFormatContext *avformatin, AVCodecContext *avcodecin, - AVFormatContext *avformatout, struct SwsContext *sws, - AVCodecContext *avcodecout) -{ - //TODO - (void)avformatin; - (void)avcodecin; - (void)avformatout; - (void)avcodecout; - (void)sws; - printf("Starting to transcode with libtuberia\n"); - printf("Transcode with libtuberia finished\n"); -} - static int decode_packet(AVCodecContext *ctx, AVPacket *packet, AVFrame *frame) { int ret = 0; + ret = avcodec_send_packet(ctx, packet); - av_packet_unref(packet); if (!ret) { ret = avcodec_receive_frame(ctx, frame); + if (!ret) { + frame->opaque = av_malloc(sizeof(int)); + *(int *)frame->opaque = packet->stream_index; + } } else { - fprintf(stderr, "Couldn't decode video packet\n"); + fprintf(stderr, "Couldn't decode video packet %s (%d)\n", + av_err2str(ret), ret); } + av_packet_unref(packet); return ret; } @@ -232,6 +217,8 @@ static int scale_frame(struct SwsContext *sws, AVFrame *frame, AVFrame *scaled, av_frame_get_buffer(scaled, 0); sws_scale(sws, (const uint8_t * const *)frame->data, frame->linesize, 0, frame->height, scaled->data, scaled->linesize); + scaled->opaque = frame->opaque; + frame->opaque = NULL; av_frame_unref(frame); return 0; @@ -242,33 +229,249 @@ static int encode_frame(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet) int ret = 0; ret = avcodec_send_frame(ctx, frame); - av_frame_unref(frame); if (!ret) { ret = avcodec_receive_packet(ctx, packet); + if (!ret && frame->opaque != NULL) { + packet->stream_index = *(int *)frame->opaque; + } } else { - fprintf(stderr, "Couldn't encode video packet\n"); + fprintf(stderr, "Couldn't encode video packet %s (%d)\n", + av_err2str(ret), ret); } + av_freep(&frame->opaque); + av_frame_unref(frame); return ret; } +static void flush_video(AVFormatContext *avformatin, AVCodecContext *avcodecin, + struct SwsContext *sws, AVCodecContext *avcodecout, + AVFormatContext *avformatout, int idx) +{ + AVFrame *frame = NULL; + AVPacket *packet = NULL; + AVFrame *scaled = NULL; + + frame = av_frame_alloc(); + packet = av_packet_alloc(); + scaled = av_frame_alloc(); + avcodec_send_packet(avcodecin, NULL); + while (!avcodec_receive_frame(avcodecin, frame)) { + scale_frame(sws, frame, scaled, avcodecout->width, + avcodecout->height, avcodecout->pix_fmt); + avcodec_send_frame(avcodecout, scaled); + if (!encode_frame(avcodecout, scaled, packet)) { + av_packet_rescale_ts(packet, avformatin->streams[idx]->time_base, + avformatout->streams[idx]->time_base); + packet->stream_index = idx; + av_interleaved_write_frame(avformatout, packet); + } + } + avcodec_send_frame(avcodecout, NULL); + while (!avcodec_receive_packet(avcodecout, packet)) { + av_packet_rescale_ts(packet, avformatin->streams[idx]->time_base, + avformatout->streams[idx]->time_base); + packet->stream_index = idx; + av_interleaved_write_frame(avformatout, packet); + } + av_frame_free(&frame); + av_packet_free(&packet); + av_frame_free(&scaled); +} + +void tube_packet_free(void *element) +{ + av_packet_free((AVPacket **)&element); +} + +void tube_frame_free(void *element) +{ + av_frame_free((AVFrame **)&element); +} + +static void *tube_decode(void *element, void *opaque) +{ + AVPacket *packet = element; + AVCodecContext *avcodecin = opaque; + AVFrame *frame = av_frame_alloc(); + + if (decode_packet(avcodecin, packet, frame)) { + av_frame_free(&frame); + } + av_packet_free(&packet); + + return frame; +} + +struct tube_resize_opaque { + struct SwsContext *sws; + int width; + int height; + enum AVPixelFormat pix_fmt; +}; + +static void *tube_resize(void *element, void *opaque) +{ + AVFrame *frame = element; + struct tube_resize_opaque *o = opaque; + AVFrame *scaled = av_frame_alloc(); + + scale_frame(o->sws, frame, scaled, o->width, o->height, o->pix_fmt); + av_frame_free(&frame); + + return scaled; +} + +static void *tube_encode(void *element, void *opaque) +{ + AVFrame *frame = element; + AVCodecContext *avcodecout = opaque; + AVPacket *packet = av_packet_alloc(); + + if (encode_frame(avcodecout, frame, packet)) { + av_packet_free(&packet); + } + av_frame_free(&frame); + + return packet; +} + +struct tube_mux_opaque { + AVFormatContext *avformatin; + AVFormatContext *avformatout; +}; + +void tube_mux(void *element, void *opaque) +{ + AVPacket *packet = element; + struct tube_mux_opaque *o = opaque; + AVPacket packet2mux; + int idx = packet->stream_index; + + av_packet_rescale_ts(packet, o->avformatin->streams[idx]->time_base, + o->avformatout->streams[idx]->time_base); + av_packet_ref(&packet2mux, packet); + av_interleaved_write_frame(o->avformatout, &packet2mux); + av_packet_free(&packet); +} + +static void print_tube_stats(tube *ctx) +{ + int queued_read = tube_get_queued(ctx, 0); + int slots_read = tube_get_slots(ctx, 0); + int queued_decode = tube_get_queued(ctx, 1); + int slots_decode = tube_get_slots(ctx, 1); + int queued_resize = tube_get_queued(ctx, 2); + int slots_resize = tube_get_slots(ctx, 2); + int queued_encode = tube_get_queued(ctx, 3); + int slots_encode = tube_get_slots(ctx, 3); + + printf("read -> [%d/%d] -> deco -> [%d/%d] -> resize -> [%d/%d] -> encode -> [%d/%d] -> write\n", + queued_read, slots_read, queued_decode, slots_decode, + queued_resize, slots_resize, queued_encode, slots_encode); +} + +static tube *build_tuberia(AVCodecContext *avcodecin, AVCodecContext *avcodecout, + struct tube_resize_opaque *tube_resize_opaque, + struct tube_mux_opaque *tube_mux_opaque) +{ + tube *ctx = NULL; + tube_source *source = NULL; + tube_stage *decode = NULL; + tube_stage *resize = NULL; + tube_stage *encode = NULL; + + source = tube_source_alloc(10, NULL, NULL, tube_packet_free); + decode = tube_stage_alloc(10, tube_decode, avcodecin, tube_frame_free); + resize = tube_stage_alloc(10, tube_resize, tube_resize_opaque, tube_frame_free); + encode = tube_stage_alloc(10, tube_encode, avcodecout, tube_packet_free); + + if (tube_stage_append(source, decode)) { + fprintf(stderr, "Couldn't append decode stage to tuberia\n"); + return NULL; + } + if (tube_stage_append(source, resize)) { + fprintf(stderr, "Couldn't append resize stage to tuberia\n"); + return NULL; + } + if (tube_stage_append(source, encode)) { + fprintf(stderr, "Couldn't append encode stage to tuberia\n"); + return NULL; + } + ctx = tube_alloc(source, tube_mux, tube_mux_opaque); + tube_source_and_stages_free(&source); + + return ctx; +} + +static void do_tuberia(AVFormatContext *avformatin, AVCodecContext *avcodecin, + AVFormatContext *avformatout, struct SwsContext *sws, + AVCodecContext *avcodecout, int video_idx) +{ + tube *ctx = NULL; + AVPacket *packet = NULL; + int idx = 0; + struct tube_resize_opaque tube_resize_opaque = { + .sws = sws, + .width = avcodecout->width, + .height = avcodecout->height, + .pix_fmt = avcodecout->pix_fmt + }; + struct tube_mux_opaque tube_mux_opaque = { + .avformatin = avformatin, + .avformatout = avformatout, + }; + + ctx = build_tuberia(avcodecin, avcodecout, &tube_resize_opaque, + &tube_mux_opaque); + if (ctx == NULL) { + fprintf(stderr, "Couldn't create tuberia\n"); + return; + } + + printf("Starting to transcode with libtuberia\n"); + tube_start(ctx); + packet = av_packet_alloc(); + while (!av_read_frame(avformatin, packet)) { + idx = packet->stream_index; + if (idx == video_idx) { + if (tube_inject(ctx, 15000, packet)) { + fprintf(stderr, "Couldn't inject packet to tuberia after 15s\n"); + break; + } + print_tube_stats(ctx); + } else { + tube_inject_at(ctx, 3, 15000, packet); + } + packet = av_packet_alloc(); + } + tube_stop_and_wait_empty(ctx); + if (video_idx >= 0) { + flush_video(avformatin, avcodecin, sws, avcodecout, avformatout, video_idx); + } + printf("Transcode with libtuberia finished\n"); + + av_packet_free(&packet); + tube_free(&ctx); +} + static void do_no_tuberia(AVFormatContext *avformatin, AVCodecContext *avcodecin, AVFormatContext *avformatout, struct SwsContext *sws, - AVCodecContext *avcodecout) + AVCodecContext *avcodecout, int video_idx) { AVPacket *packet = NULL; AVFrame *frame = NULL; AVFrame *scaled = NULL; int idx = 0; - printf("Starting to transcode without libtuberia\n"); - packet = av_packet_alloc(); frame = av_frame_alloc(); scaled = av_frame_alloc(); + + printf("Starting to transcode without libtuberia\n"); while (!av_read_frame(avformatin, packet)) { idx = packet->stream_index; - if (avformatin->streams[idx]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + if (idx == video_idx) { if (decode_packet(avcodecin, packet, frame)) { continue; } @@ -277,17 +480,19 @@ static void do_no_tuberia(AVFormatContext *avformatin, AVCodecContext *avcodecin if (encode_frame(avcodecout, scaled, packet)) { continue; } - packet->stream_index = idx; } av_packet_rescale_ts(packet, avformatin->streams[idx]->time_base, avformatout->streams[idx]->time_base); av_interleaved_write_frame(avformatout, packet); } + if (video_idx >= 0) { + flush_video(avformatin, avcodecin, sws, avcodecout, avformatout, video_idx); + } + printf("Transcode without libtuberia finished\n"); + av_packet_free(&packet); av_frame_free(&frame); av_frame_free(&scaled); - - printf("Transcode without libtuberia finished\n"); } int main(int argc, char **argv) @@ -303,6 +508,7 @@ int main(int argc, char **argv) AVCodecContext *avcodecin = NULL; AVCodecContext *avcodecout = NULL; struct SwsContext *sws = NULL; + int video_idx = -1; while ((c = getopt(argc, argv, optstring)) >= 0) { switch (c) { @@ -337,7 +543,7 @@ int main(int argc, char **argv) return -1; } - if (open_input(&avformatin, &avcodecin, input) < 0) { + if (open_input(&avformatin, &avcodecin, &video_idx, input) < 0) { fprintf(stderr, "Couldn't open input %s\n", input); return -1; } @@ -348,9 +554,9 @@ int main(int argc, char **argv) } if (notuberia) { - do_no_tuberia(avformatin, avcodecin, avformatout, sws, avcodecout); + do_no_tuberia(avformatin, avcodecin, avformatout, sws, avcodecout, video_idx); } else { - do_tuberia(avformatin, avcodecin, avformatout, sws, avcodecout); + do_tuberia(avformatin, avcodecin, avformatout, sws, avcodecout, video_idx); } close_input(&avformatin); |