Skip to content

Add example CLI project #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake_minimum_required (VERSION 3.9)
include(CMakePrintHelpers)

set(FFMPEG_NUGET_NAME "ffmpeg-lgpl")
set(FFMPEG_NUGET_VERSION "6.1.0.2")
Expand All @@ -19,6 +20,12 @@ endif(NOT WIN32)
file(READ "VERSION" PROJECT_FILE_VERSION)
project(Griffeye.VideoProcessor.Native VERSION ${PROJECT_FILE_VERSION} DESCRIPTION "Video frame extraction library using ffmpeg")

option(BUILD_EXAMPLES "build examples" OFF)

if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Debug")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building from the command line was not working (cmake -S . -B build_linux/) since CMAKE_BUILD_TYPE was not set at that stage. That mean that the ffmpeg packages couldn't be found because the path was incomplete.

endif()

add_library(${PROJECT_NAME} SHARED
src/libvx.c
src/filter.c
Expand All @@ -42,16 +49,15 @@ if(WIN32)
PROPERTY VS_PACKAGE_REFERENCES "${FFMPEG_NUGET_NAME}_${FFMPEG_NUGET_VERSION}"
)
else()
string(TOLOWER ${CMAKE_BUILD_TYPE} build_type)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_TYPE)

SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
target_include_directories(${PROJECT_NAME} PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/Packages/${FFMPEG_NUGET_NAME}/${FFMPEG_NUGET_VERSION}/build/native/Unix/include/x64"
"${CMAKE_CURRENT_SOURCE_DIR}/Packages/${FFMPEG_NUGET_NAME}/${FFMPEG_NUGET_VERSION}/build/native/Unix/include/x64"
)

# Assumes FFMpeg NuGet package have been explicitly/externally restored to ${CMAKE_CURRENT_SOURCE_DIR}/Packages
set(FFMPEG_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Packages/${FFMPEG_NUGET_NAME}/${FFMPEG_NUGET_VERSION}/build/native/Unix/lib/${build_type}/x64")
include(CMakePrintHelpers)
set(FFMPEG_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Packages/${FFMPEG_NUGET_NAME}/${FFMPEG_NUGET_VERSION}/build/native/Unix/lib/${BUILD_TYPE}/x64")
cmake_print_variables(FFMPEG_LIB_PATH)

find_library(AVUTIL_LIBRARY NAMES avutil PATHS ${FFMPEG_LIB_PATH} REQUIRED NO_DEFAULT_PATH NO_CACHE)
Expand All @@ -65,11 +71,10 @@ else()
find_library(PKGCONF_LIBRARY NAMES pkgconf PATHS ${FFMPEG_LIB_PATH} REQUIRED NO_DEFAULT_PATH NO_CACHE)
find_library(DAV1D_LIBRARY NAMES dav1d PATHS ${FFMPEG_LIB_PATH} REQUIRED NO_DEFAULT_PATH NO_CACHE)
find_library(Z_LIBRARY NAMES z PATHS ${FFMPEG_LIB_PATH} REQUIRED NO_DEFAULT_PATH NO_CACHE)


set(FFMPEG_LIBS ${AVDEVICE_LIBRARY} ${AVFILTER_LIBRARY} ${AVFORMAT_LIBRARY} ${AVCODEC_LIBRARY} ${SWRESAMPLE_LIBRARY} ${SWSCALE_LIBRARY} ${AVUTIL_LIBRARY} ${VPX_LIBRARY} ${PKGCONF_LIBRARY} ${DAV1D_LIBRARY} ${Z_LIBRARY})
cmake_print_variables(FFMPEG_LIBS)


# Provide source code for gdb
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdebug-prefix-map=/build/build_linux_debug=/usr/src/griffeye.videoprocessor")

set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-Bsymbolic")
Expand All @@ -82,6 +87,9 @@ set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER include/libvx.h)

# Include sub-projects.
add_subdirectory("src")
if (BUILD_EXAMPLES)
add_subdirectory("examples")
endif()

# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,21 @@ Need to have dotnet CLI installed and some build utils
`RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y build-essential cmake zip`

```
dotnet restore Griffeye.VideoProcessor.Native.csproj --packages Packages
dotnet restore Griffeye.VideoProcessor.Native.csproj --configfile NuGet.Config --packages Packages
cmake -S . -B build_linux/
cmake --build build_linux/ --config [Release|Debug]
```

### Examples
Projects from the `/examples` directory can be built by adding `-DBUILD_EXAMPLES=ON` to the above cmake command:
```
cmake -S . -B build_linux/ -DBUILD_EXAMPLES=ON
```

### Testing
The project can be run with Valgrind via the CLI example project:
```
valgrind --leak-check=full libvxcli video.mp4
```

## Architecture
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0
1.0.2
11 changes: 11 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
cmake_minimum_required (VERSION 3.9)

set(TARGET libvxcli)

include_directories("${CMAKE_SOURCE_DIR}/src")

add_executable(${TARGET} cli.c)

target_link_libraries(${TARGET} PRIVATE ${FFMPEG_LIBS})
target_link_libraries(${TARGET} PRIVATE Griffeye.VideoProcessor.Native)
target_link_libraries(${TARGET} PRIVATE m)
77 changes: 77 additions & 0 deletions examples/cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>

#include "libvx.h"

int main(int argc, char* argv[]) {
int result = 1;
char* video_path = argc > 1 ? argv[1] : "";
vx_video* video = NULL;
vx_video_info* video_info = calloc(1, sizeof(vx_video_info));
vx_frame_info* frame_info = calloc(1, sizeof(vx_frame_info));
vx_frame* frame = NULL;

vx_audio_params audio_params = {
.channels = 2,
.sample_format = VX_SAMPLE_FMT_FLT,
.sample_rate = 44100
};
const vx_video_options options = {
.audio_params = audio_params,
.autorotate = true,
.crop_area = {0},
.hw_criteria = VX_HW_ACCEL_ALL,
.scene_threshold = 0.5
};

if (vx_open(video_path, options, &video, video_info) != VX_ERR_SUCCESS) {
printf("Failed to open video: %s\n", video_path);
goto cleanup;
}

// Initialize an empty frame that will be reused throughout the video
frame = vx_frame_create(video, video_info->width, video_info->height, VX_PIX_FMT_RGB32);
if (!frame) {
printf("Failed to create frame\n");
goto cleanup;
}

// Iterate over all frames in the video. The frame is decoded when stepping
while (vx_frame_step(video, frame_info) == VX_ERR_SUCCESS) {
bool is_video_frame = frame_info->flags & VX_FF_HAS_IMAGE;

printf("Frame: %ld, %dx%d\n", video->frame_count, frame_info->width, frame_info->height);

// Perform processing and tranfer from GPU to CPU if necessary
if (vx_frame_transfer_data(video, frame) != VX_ERR_SUCCESS) {
printf("Failed to transfer frame data\n");
goto cleanup;
}
else {
printf("Frame type: %s\n", is_video_frame ? "video" : "audio");
// The frame image could be accessed via frame->buffer
// Alternatively for audio frames, the audio samples could be accessed via frame->audio_data
}
}

result = 0;

cleanup:
vx_close(video);
if (video_info) {
free(video_info);
}
if (frame) {
vx_frame_destroy(frame);
}
if (frame_info) {
free(frame_info);
}

return result;
}
2 changes: 1 addition & 1 deletion src/filtergraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extern "C" {
#endif

vx_error vx_filtergraph_init(struct AVFilterGraph** filter_graph, enum AVMediaType type, const char* args);
vx_error vx_filtergraph_configure(struct AVFilterGraph** filter_graph, enum AVMediaType mediaType, struct AVFilterContext** last_filter, int* pad_index);
vx_error vx_filtergraph_configure(struct AVFilterGraph** filter_graph, enum AVMediaType type, struct AVFilterContext** last_filter, int* pad_index);
vx_error vx_filtergraph_insert_filter(struct AVFilterContext** last_filter, int* pad_index, const char* filter_name, const char* filter_label, const char* args);
vx_error vx_filtergraph_process_frame(struct AVFilterGraph** filter_graph, AVFrame* av_frame);

Expand Down
80 changes: 26 additions & 54 deletions src/libvx.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,69 +31,32 @@
static bool initialized = false;
static vx_log_callback log_cb = NULL;

struct vx_audio_info
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to move these to the header file so they could be used in the new CLI

{
double peak_level;
double rms_level;
double rms_peak;
};

struct vx_scene_info
{
double difference;
double scene_score;
bool new_scene;
};

struct vx_frame
{
int width;
int height;
vx_pix_fmt pix_fmt;
int sample_count;
int max_samples;

vx_audio_info audio_info;
vx_scene_info scene_info;

uint8_t** audio_buffer;
void* buffer;
};

struct vx_frame_info
{
int width;
int height;
double timestamp;
vx_frame_flag flags;
};

static vx_log_level av_to_vx_log_level(const int level)
{
// See: lavu_log_constants
switch (level) {
case AV_LOG_QUIET:
return VX_LOG_NONE;
case AV_LOG_QUIET:
return VX_LOG_NONE;

case AV_LOG_PANIC:
case AV_LOG_FATAL:
return VX_LOG_FATAL;
case AV_LOG_PANIC:
case AV_LOG_FATAL:
return VX_LOG_FATAL;

case AV_LOG_ERROR:
return VX_LOG_ERROR;
case AV_LOG_ERROR:
return VX_LOG_ERROR;

case AV_LOG_WARNING:
return VX_LOG_WARNING;
case AV_LOG_WARNING:
return VX_LOG_WARNING;

case AV_LOG_INFO:
case AV_LOG_VERBOSE:
return VX_LOG_INFO;
case AV_LOG_INFO:
case AV_LOG_VERBOSE:
return VX_LOG_INFO;

case AV_LOG_DEBUG:
return VX_LOG_DEBUG;
case AV_LOG_DEBUG:
return VX_LOG_DEBUG;

default:
return VX_LOG_NONE;
default:
return VX_LOG_NONE;
}
}

Expand Down Expand Up @@ -558,13 +521,22 @@ void vx_close(vx_video* video)
swr_free(&video->swr_ctx);

if (video->fmt_ctx)
avformat_free_context(video->fmt_ctx);
avformat_close_input(&video->fmt_ctx);

for (int i = 0; i < video->frame_queue_count; i++) {
av_frame_unref(video->frame_queue[i]);
av_frame_free(&video->frame_queue[i]);
}

if (video->hw_device_ctx)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the "måsvinge"?

av_buffer_unref(&video->hw_device_ctx);

if (video->audio_codec_ctx)
avcodec_free_context(&video->audio_codec_ctx);

if (video->video_codec_ctx)
avcodec_free_context(&video->video_codec_ctx);

free(video);
}

Expand Down
43 changes: 40 additions & 3 deletions src/libvx.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ extern "C" {
#endif

#ifdef _MSC_VER
// Microsoft
// Microsoft
#define VX_DECLSPEC __declspec(dllexport)
#define VX_CDECL __cdecl
#else
// GCC
// GCC
#define VX_DECLSPEC __attribute__((visibility("default")))
#define VX_CDECL __attribute__((__cdecl__))
#define VX_CDECL

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure this change doesn't cause any issues on Linux

#endif

#define FRAME_QUEUE_SIZE 32
Expand Down Expand Up @@ -120,6 +120,43 @@ struct av_audio_params
AVRational time_base;
};

struct vx_audio_info
{
double peak_level;
double rms_level;
double rms_peak;
};

struct vx_scene_info
{
double difference;
double scene_score;
bool new_scene;
};

struct vx_frame_info
{
int width;
int height;
double timestamp;
vx_frame_flag flags;
};

struct vx_frame
{
int width;
int height;
vx_pix_fmt pix_fmt;
int sample_count;
int max_samples;

vx_audio_info audio_info;
vx_scene_info scene_info;

uint8_t** audio_buffer;
void* buffer;
};

struct vx_video_options
{
vx_audio_params audio_params;
Expand Down