From bd9b4f8288878b38823aced0dbb86564070ae0c1 Mon Sep 17 00:00:00 2001 From: Steins7 Date: Sun, 22 Sep 2024 23:12:43 +0200 Subject: [PATCH] Start custom ffmpeg implementation For now, only device scanning is working --- Makefile | 2 +- src/ffmpeg.c | 98 ++++++++++++++++++++++ src/ffmpeg.h | 22 +++++ src/main.c | 228 ++------------------------------------------------- 4 files changed, 126 insertions(+), 224 deletions(-) create mode 100644 src/ffmpeg.c create mode 100644 src/ffmpeg.h diff --git a/Makefile b/Makefile index 433ccd7..c5dbaf5 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ C_DEFS= #list all ASM defines here with -D before the name A_DEFS= #list all linker flags here with -l before the name -LDFLAGS=-lavcodec -lavformat -lavutil -lavdevice +LDFLAGS=-lavcodec -lavformat -lavutil -lavdevice -lv4l2 # --- general settings # RELEASE=0 -> disable optimisation, then enable debug diff --git a/src/ffmpeg.c b/src/ffmpeg.c new file mode 100644 index 0000000..d55325f --- /dev/null +++ b/src/ffmpeg.c @@ -0,0 +1,98 @@ +/** @file ffmpeg.c + * + */ + +//--includes-------------------------------------------------------------------- + +#include +#include //required by videodev2.h +#include +#include +#include +#include +#include +#include + + +//--local definitions----------------------------------------------------------- + +const AVDeviceInfo* find_h264_device(const AVDeviceInfoList* device_list); + + +//--local variables------------------------------------------------------------- + +//--public functions------------------------------------------------------------ + +int ffmpeg_init(void) +{ + avdevice_register_all(); + + const AVInputFormat* v4l2_format = av_find_input_format("v4l2"); + AVDeviceInfoList* device_info_list; + int device_nb = avdevice_list_input_sources(v4l2_format, nullptr, nullptr, + &device_info_list); + if (device_nb < 0) { + fprintf(stderr, "Failed to list available devices\n"); + avdevice_free_list_devices(&device_info_list); + return -1; + } else if (device_nb == 0) { + fprintf(stderr, "No compatible input device found\n"); + avdevice_free_list_devices(&device_info_list); + return -1; + } + + const AVDeviceInfo* device = find_h264_device(device_info_list); + if (device == nullptr) { + fprintf(stderr, "No H264 device found\n"); + avdevice_free_list_devices(&device_info_list); + return -1; + } + + printf("Found compatible device: %s\n%s\n", + device->device_name, + device->device_description); + + avdevice_free_list_devices(&device_info_list); + return 0; +} + +void ffmpeg_deinit() +{ +} + +//--local functions------------------------------------------------------------- + +const AVDeviceInfo* find_h264_device(const AVDeviceInfoList* device_list) +{ + for (int i=0; inb_devices; ++i) { + const AVDeviceInfo* device = device_list->devices[i]; + + int fd; + if ((fd = v4l2_open(device->device_name, O_RDWR)) < 0) { + fprintf(stderr, "Failed to open: %s, ignoring...\n", + device->device_name); + continue; + } + + bool found = false; + struct v4l2_fmtdesc fmt; + fmt.index = 0; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + while (v4l2_ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { + if (fmt.pixelformat == V4L2_PIX_FMT_H264) { + found = true; + break; + } + + ++fmt.index; + } + + v4l2_close(fd); + if (found == true) { + return device; + } + } + + return nullptr; +} + diff --git a/src/ffmpeg.h b/src/ffmpeg.h new file mode 100644 index 0000000..2dd992e --- /dev/null +++ b/src/ffmpeg.h @@ -0,0 +1,22 @@ +/** @file ffmpeg.h + * + */ + +//--includes-------------------------------------------------------------------- + +#include + + +//--local definitions----------------------------------------------------------- + +//--local variables------------------------------------------------------------- + +//--public functions------------------------------------------------------------ + +int ffmpeg_init(); +int ffmpeg_deinit(); +int ffmpeg_read_frame(uint8_t* some_data); + + +//--local functions------------------------------------------------------------- + diff --git a/src/main.c b/src/main.c index 1ffc721..714997d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,228 +1,10 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "ffmpeg.h" -static AVBufferRef *hw_device_ctx = NULL; -static enum AVPixelFormat hw_pix_fmt; -static FILE *output_file = NULL; - -static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) +int main(void) { - int err = 0; + ffmpeg_init(); + ffmpeg_deinit(); - if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, - NULL, NULL, 0)) < 0) { - fprintf(stderr, "Failed to create specified HW device.\n"); - return err; - } - ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); - - return err; + return 0; } - -static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, - const enum AVPixelFormat *pix_fmts) -{ - const enum AVPixelFormat *p; - - for (p = pix_fmts; *p != -1; p++) { - if (*p == hw_pix_fmt) - return *p; - } - - fprintf(stderr, "Failed to get HW surface format.\n"); - return AV_PIX_FMT_NONE; -} - -static int decode_write(AVCodecContext *avctx, AVPacket *packet) -{ - AVFrame *frame = NULL, *sw_frame = NULL; - AVFrame *tmp_frame = NULL; - uint8_t *buffer = NULL; - int size; - int ret = 0; - - ret = avcodec_send_packet(avctx, packet); - if (ret < 0) { - fprintf(stderr, "Error during decoding\n"); - return ret; - } - - while (1) { - if (!(frame = av_frame_alloc()) || !(sw_frame = av_frame_alloc())) { - fprintf(stderr, "Can not alloc frame\n"); - ret = AVERROR(ENOMEM); - goto fail; - } - - ret = avcodec_receive_frame(avctx, frame); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { - av_frame_free(&frame); - av_frame_free(&sw_frame); - return 0; - } else if (ret < 0) { - fprintf(stderr, "Error while decoding\n"); - goto fail; - } - - if (frame->format == hw_pix_fmt) { - /* retrieve data from GPU to CPU */ - if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) { - fprintf(stderr, "Error transferring the data to system memory\n"); - goto fail; - } - tmp_frame = sw_frame; - } else - tmp_frame = frame; - - size = av_image_get_buffer_size(tmp_frame->format, tmp_frame->width, - tmp_frame->height, 1); - buffer = av_malloc(size); - if (!buffer) { - fprintf(stderr, "Can not alloc buffer\n"); - ret = AVERROR(ENOMEM); - goto fail; - } - ret = av_image_copy_to_buffer(buffer, size, - (const uint8_t * const *)tmp_frame->data, - (const int *)tmp_frame->linesize, tmp_frame->format, - tmp_frame->width, tmp_frame->height, 1); - if (ret < 0) { - fprintf(stderr, "Can not copy image to buffer\n"); - goto fail; - } - - if ((ret = fwrite(buffer, 1, size, output_file)) < 0) { - fprintf(stderr, "Failed to dump raw data.\n"); - goto fail; - } - - fail: - av_frame_free(&frame); - av_frame_free(&sw_frame); - av_freep(&buffer); - if (ret < 0) - return ret; - } -} - -int main(int argc, char *argv[]) -{ - AVFormatContext *input_ctx = NULL; - int video_stream, ret; - AVStream *video = NULL; - AVCodecContext *decoder_ctx = NULL; - const AVCodec *decoder = NULL; - AVPacket *packet = NULL; - enum AVHWDeviceType type; - int i; - - if (argc < 4) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return -1; - } - - avdevice_register_all(); - - type = av_hwdevice_find_type_by_name(argv[1]); - if (type == AV_HWDEVICE_TYPE_NONE) { - fprintf(stderr, "Device type %s is not supported.\n", argv[1]); - fprintf(stderr, "Available device types:"); - while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) - fprintf(stderr, " %s", av_hwdevice_get_type_name(type)); - fprintf(stderr, "\n"); - return -1; - } - - packet = av_packet_alloc(); - if (!packet) { - fprintf(stderr, "Failed to allocate AVPacket\n"); - return -1; - } - - /* open the input file */ - const AVInputFormat* input_format = av_find_input_format("v4l2"); - if (avformat_open_input(&input_ctx, argv[2], input_format, NULL) != 0) { - fprintf(stderr, "Cannot open input file '%s'\n", argv[2]); - return -1; - } - - if (avformat_find_stream_info(input_ctx, NULL) < 0) { - fprintf(stderr, "Cannot find input stream information.\n"); - return -1; - } - - /* find the video stream information */ - ret = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); - if (ret < 0) { - fprintf(stderr, "Cannot find a video stream in the input file\n"); - return -1; - } - video_stream = ret; - - for (i = 0;; i++) { - const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i); - if (!config) { - fprintf(stderr, "Decoder %s does not support device type %s.\n", - decoder->name, av_hwdevice_get_type_name(type)); - return -1; - } - if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && - config->device_type == type) { - hw_pix_fmt = config->pix_fmt; - break; - } - } - - if (!(decoder_ctx = avcodec_alloc_context3(decoder))) - return AVERROR(ENOMEM); - - video = input_ctx->streams[video_stream]; - if (avcodec_parameters_to_context(decoder_ctx, video->codecpar) < 0) - return -1; - - decoder_ctx->get_format = get_hw_format; - - if (hw_decoder_init(decoder_ctx, type) < 0) - return -1; - - if ((ret = avcodec_open2(decoder_ctx, decoder, NULL)) < 0) { - fprintf(stderr, "Failed to open codec for stream #%u\n", video_stream); - return -1; - } - - /* open the file to dump raw data */ - output_file = fopen(argv[3], "w+b"); - - /* actual decoding and dump the raw data */ - while (ret >= 0) { - if ((ret = av_read_frame(input_ctx, packet)) < 0) - break; - - if (video_stream == packet->stream_index) - ret = decode_write(decoder_ctx, packet); - - av_packet_unref(packet); - } - - /* flush the decoder */ - ret = decode_write(decoder_ctx, NULL); - - if (output_file) - fclose(output_file); - av_packet_free(&packet); - avcodec_free_context(&decoder_ctx); - avformat_close_input(&input_ctx); - av_buffer_unref(&hw_device_ctx); - - return 0; -} -