[SCM] nordlicht/master: New upstream version 0.4.5
cinemast-guest at users.alioth.debian.org
cinemast-guest at users.alioth.debian.org
Fri Jan 13 22:45:51 UTC 2017
The following commit has been merged in the master branch:
commit 1e7979dd4fb3354059fe0b839a007e88bc541d92
Author: Peter Spiess-Knafl <dev at spiessknafl.at>
Date: Fri Jan 13 23:28:27 2017 +0100
New upstream version 0.4.5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index baed063..3973ae6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
*nordlicht* uses [Semantic Versioning](http://semver.org/).
+## 0.4.5 - 2017-01-09
+
+- introduce `nordlicht_generate_step` and `nordlicht_done` to the API, for simple, non-blocking applications
+- improve FFmpeg backwards compability down to v3.0 (thanks, Manuel!)
+- fix build on ARM platforms
+- correctly open files which contain a colon (thanks, Roland!)
+- various bug fixes and stability improvements
+
+- tool: when the user specifies a non-PNG file as output, warn them but don't fail
+
## 0.4.4 - 2016-01-24
- introduce the convention that the nordlicht is always written to `VIDEOFILE.nordlicht.png`
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad3eb00..c181d90 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8)
project(nordlicht C)
set(MAJOR_VERSION 0)
set(MINOR_VERSION 4)
-set(PATCH_VERSION 4)
+set(PATCH_VERSION 5)
set(NORDLICHT_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
option (BUILD_SHARED_LIBS "Build shared libraries." ON)
@@ -12,6 +12,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
include(GNUInstallDirs)
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
+
if (BUILD_SHARED_LIBS)
add_definitions(-DNORDLICHT_BUILD_SHARED)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
@@ -20,7 +22,6 @@ else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
endif()
-find_package(Threads REQUIRED)
find_package(FFmpeg COMPONENTS AVUTIL AVFORMAT AVCODEC SWSCALE REQUIRED)
find_package(Popt REQUIRED)
@@ -33,22 +34,22 @@ endif()
include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR} ${FFMPEG_INCLUDE_DIRS} ${POPT_INCLUDES})
-add_library(libnordlicht error.c image.c nordlicht.c source.c)
+add_library(libnordlicht src/error.c src/image.c src/nordlicht.c src/source.c)
set_target_properties(libnordlicht PROPERTIES OUTPUT_NAME nordlicht)
set_target_properties(libnordlicht PROPERTIES SOVERSION ${MAJOR_VERSION} VERSION ${NORDLICHT_VERSION})
set_target_properties(libnordlicht PROPERTIES C_VISIBILITY_PRESET hidden)
target_link_libraries(libnordlicht m ${FFMPEG_LIBRARIES})
-add_executable(nordlicht main.c)
-target_link_libraries(nordlicht libnordlicht ${POPT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+add_executable(nordlicht src/main.c)
+target_link_libraries(nordlicht libnordlicht ${POPT_LIBRARIES})
if (WIN32)
target_link_libraries(nordlicht mman)
endif()
-add_executable(testsuite testsuite.c)
+add_executable(testsuite src/testsuite.c)
target_link_libraries(testsuite libnordlicht)
-configure_file(version.h.in version.h @ONLY)
+configure_file(src/version.h.in version.h @ONLY)
if (NOT WIN32)
configure_file(cmake/nordlicht.pc.in nordlicht.pc @ONLY)
@@ -60,10 +61,10 @@ endif()
install(TARGETS libnordlicht DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS nordlicht DESTINATION bin)
-install(FILES nordlicht.h DESTINATION include)
+install(FILES ${CMAKE_SOURCE_DIR}/src/nordlicht.h DESTINATION include)
if (NOT WIN32)
- install(FILES ${CMAKE_BINARY_DIR}/nordlicht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+ install(FILES ${CMAKE_BINARY_DIR}/nordlicht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
install(FILES ${CMAKE_BINARY_DIR}/nordlicht.1 DESTINATION share/man/man1)
endif()
diff --git a/README.md b/README.md
index 221b14b..9d13d0a 100644
--- a/README.md
+++ b/README.md
@@ -21,4 +21,4 @@ You can also help by packaging the software for your favorite operating system,
## License: GPLv2+
-See [LICENSE.md] for details.
+See [LICENSE.md](LICENSE.md) for details.
diff --git a/cmake/download_testfile.cmake b/cmake/download_testfile.cmake
index 49d69b6..aea9108 100644
--- a/cmake/download_testfile.cmake
+++ b/cmake/download_testfile.cmake
@@ -1,4 +1,4 @@
if(NOT EXISTS ${CMAKE_BINARY_DIR}/video.mp4)
- message("Downloading 20 MB test file, just a sec...")
- file(DOWNLOAD https://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv ${CMAKE_BINARY_DIR}/video.mp4 EXPECTED_MD5 9f701e645fd55e1ae8d35b7671002881)
+ message("Downloading 3 MB test file, just a sec...")
+ file(DOWNLOAD https://upload.wikimedia.org/wikipedia/commons/0/07/Sintel_excerpt.OGG ${CMAKE_BINARY_DIR}/video.mp4 EXPECTED_MD5 be22cb1f83f6f3620d8048df12f9f52d)
endif()
diff --git a/nordlicht.c b/nordlicht.c
deleted file mode 100644
index e696847..0000000
--- a/nordlicht.c
+++ /dev/null
@@ -1,331 +0,0 @@
-#include "nordlicht.h"
-#include <string.h>
-#include "error.h"
-#include "source.h"
-
-#ifdef _WIN32
-#define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
-#endif
-
-typedef struct {
- nordlicht_style style;
- int height;
-} track;
-
-struct nordlicht {
- int width, height;
- const char *filename;
- track *tracks;
- int num_tracks;
- unsigned char *data;
-
- int owns_data;
- int modifiable;
- nordlicht_strategy strategy;
- float progress;
- source *source;
-};
-
-NORDLICHT_API size_t nordlicht_buffer_size(const nordlicht *n) {
- return n->width * n->height * 4;
-}
-
-NORDLICHT_API nordlicht* nordlicht_init(const char *filename, const int width, const int height) {
- if (width < 1 || height < 1) {
- error("Dimensions must be positive (got %dx%d)", width, height);
- return NULL;
- }
- nordlicht *n;
- n = (nordlicht *) malloc(sizeof(nordlicht));
-
- n->width = width;
- n->height = height;
- n->filename = filename;
-
- n->data = (unsigned char *) calloc(nordlicht_buffer_size(n), 1);
- if (n->data == 0) {
- error("Not enough memory to allocate %d bytes", nordlicht_buffer_size(n));
- return NULL;
- }
-
- n->owns_data = 1;
-
- n->num_tracks = 1;
- n->tracks = (track *) malloc(sizeof(track));
- n->tracks[0].style = NORDLICHT_STYLE_HORIZONTAL;
- n->tracks[0].height = n->height;
-
- n->strategy = NORDLICHT_STRATEGY_FAST;
- n->modifiable = 1;
- n->progress = 0;
- n->source = source_init(filename);
-
- if (n->source == NULL) {
- error("Could not open video file '%s'", filename);
- free(n);
- return NULL;
- }
-
- return n;
-}
-
-NORDLICHT_API void nordlicht_free(nordlicht *n) {
- if (n->owns_data) {
- free(n->data);
- }
- free(n->tracks);
- source_free(n->source);
- free(n);
-}
-
-NORDLICHT_API const char *nordlicht_error() {
- return get_error();
-}
-
-NORDLICHT_API int nordlicht_set_start(nordlicht *n, const float start) {
- if (! n->modifiable) {
- return -1;
- }
-
- if (start < 0) {
- error("'start' has to be >= 0.");
- return -1;
- }
-
- if (start >= source_end(n->source)) {
- error("'start' has to be smaller than 'end'.");
- return -1;
- }
-
- source_set_start(n->source, start);
- return 0;
-}
-
-NORDLICHT_API int nordlicht_set_end(nordlicht *n, const float end) {
- if (! n->modifiable) {
- return -1;
- }
-
- if (end > 1) {
- error("'end' has to be <= 1.");
- return -1;
- }
-
- if (source_start(n->source) >= end) {
- error("'start' has to be smaller than 'end'.");
- return -1;
- }
-
- source_set_end(n->source, end);
- return 0;
-}
-
-NORDLICHT_API int nordlicht_set_style(nordlicht *n, const nordlicht_style style) {
- if (! n->modifiable) {
- return -1;
- }
-
- nordlicht_style styles[1] = {style};
- return nordlicht_set_styles(n, styles, 1);
-}
-
-NORDLICHT_API int nordlicht_set_styles(nordlicht *n, const nordlicht_style *styles, const int num_tracks) {
- if (! n->modifiable) {
- return -1;
- }
-
- n->num_tracks = num_tracks;
-
- if (n->num_tracks > n->height) {
- error("Height of %d px is too low for %d styles", n->height, n->num_tracks);
- return -1;
- }
-
- free(n->tracks);
- n->tracks = (track *) malloc(n->num_tracks*sizeof(track));
-
- int height_of_each_track = n->height/n->num_tracks;
- int i;
- for (i=0; i<num_tracks; i++) {
- nordlicht_style s = styles[i];
- if (s > NORDLICHT_STYLE_LAST-1) {
- return -1;
- }
-
- n->tracks[i].style = s;
- n->tracks[i].height = height_of_each_track;
- }
- n->tracks[0].height = n->height - (n->num_tracks-1)*height_of_each_track;
-
- return 0;
-}
-
-NORDLICHT_API int nordlicht_set_strategy(nordlicht *n, const nordlicht_strategy s) {
- if (! n->modifiable) {
- return -1;
- }
- if (s > NORDLICHT_STRATEGY_LIVE) {
- return -1;
- }
- n->strategy = s;
- return 0;
-}
-
-NORDLICHT_API int nordlicht_generate(nordlicht *n) {
- n->modifiable = 0;
-
- source_build_keyframe_index(n->source, n->width);
-
- int x, exact;
-
- const int do_a_fast_pass = (n->strategy == NORDLICHT_STRATEGY_LIVE) || !source_exact(n->source);
- const int do_an_exact_pass = source_exact(n->source);
-
- for (exact = (!do_a_fast_pass); exact <= do_an_exact_pass; exact++) {
- int i;
- int y_offset = 0;
- for(i = 0; i < n->num_tracks; i++) {
- // call this for each track, to seek to the beginning
- source_set_exact(n->source, exact);
-
- for (x = 0; x < n->width; x++) {
- image *frame;
-
- if (n->tracks[i].style == NORDLICHT_STYLE_SPECTROGRAM) {
- if (!source_has_audio(n->source)) {
- error("File contains no audio, please select an appropriate style");
- n->progress = 1;
- return -1;
- }
- frame = source_get_audio_frame(n->source, 1.0*(x+0.5-COLUMN_PRECISION/2.0)/n->width,
- 1.0*(x+0.5+COLUMN_PRECISION/2.0)/n->width);
- } else {
- if (!source_has_video(n->source)) {
- error("File contains no video, please select an appropriate style");
- n->progress = 1;
- return -1;
- }
- frame = source_get_video_frame(n->source, 1.0*(x+0.5-COLUMN_PRECISION/2.0)/n->width,
- 1.0*(x+0.5+COLUMN_PRECISION/2.0)/n->width);
- }
- if (frame == NULL) {
- continue;
- }
-
- int thumbnail_width = 1.0*(image_width(frame)*n->tracks[i].height/image_height(frame));
- image *column = NULL;
- image *tmp = NULL;
- switch (n->tracks[i].style) {
- case NORDLICHT_STYLE_THUMBNAILS:
- column = image_scale(frame, thumbnail_width, n->tracks[i].height);
- break;
- case NORDLICHT_STYLE_HORIZONTAL:
- column = image_scale(frame, 1, n->tracks[i].height);
- break;
- case NORDLICHT_STYLE_VERTICAL:
- tmp = image_scale(frame, n->tracks[i].height, 1);
- column = image_flip(tmp);
- image_free(tmp);
- break;
- case NORDLICHT_STYLE_SLITSCAN:
- tmp = image_column(frame, 1.0*(x%thumbnail_width)/thumbnail_width);
-
- column = image_scale(tmp, 1, n->tracks[i].height);
- image_free(tmp);
- break;
- case NORDLICHT_STYLE_MIDDLECOLUMN:
- tmp = image_column(frame, 0.5);
- column = image_scale(tmp, 1, n->tracks[i].height);
- image_free(tmp);
- break;
- case NORDLICHT_STYLE_SPECTROGRAM:
- column = image_scale(frame, 1, n->tracks[i].height);
- break;
- default:
- // cannot happen (TM)
- return -1;
- break;
- }
-
- image_to_bgra(n->data, n->width, n->height, column, x, y_offset);
-
- n->progress = (i+1.0*x/n->width)/n->num_tracks;
- x = x + image_width(column) - 1;
-
- image_free(column);
- }
-
- y_offset += n->tracks[i].height;
- }
- }
-
- n->progress = 1.0;
- return 0;
-}
-
-NORDLICHT_API int nordlicht_write(const nordlicht *n, const char *filename) {
- int code = 0;
-
- if (filename == NULL) {
- error("Output filename must not be NULL");
- return -1;
- }
-
- if (strcmp(filename, "") == 0) {
- error("Output filename must not be empty");
- return -1;
- }
-
- char *realpath_output = realpath(filename, NULL);
- if (realpath_output != NULL) {
- // output file exists
- char *realpath_input = realpath(n->filename, NULL);
- if (realpath_input != NULL) {
- // otherwise, input filename is probably a URL
-
- if (strcmp(realpath_input, realpath_output) == 0) {
- error("Will not overwrite input file");
- code = -1;
- }
- free(realpath_input);
- }
- free(realpath_output);
-
- if (code != 0) {
- return code;
- }
- }
-
- image *i = image_from_bgra(n->data, n->width, n->height);
- if (image_write_png(i, filename) != 0) {
- return -1;
- }
- image_free(i);
-
- return code;
-}
-
-NORDLICHT_API float nordlicht_progress(const nordlicht *n) {
- return n->progress;
-}
-
-NORDLICHT_API const unsigned char* nordlicht_buffer(const nordlicht *n) {
- return n->data;
-}
-
-NORDLICHT_API int nordlicht_set_buffer(nordlicht *n, unsigned char *data) {
- if (! n->modifiable) {
- return -1;
- }
-
- if (data == NULL) {
- return -1;
- }
-
- if (n->owns_data) {
- free(n->data);
- }
- n->owns_data = 0;
- n->data = data;
- return 0;
-}
diff --git a/cheat.h b/src/cheat.h
similarity index 100%
rename from cheat.h
rename to src/cheat.h
diff --git a/error.c b/src/error.c
similarity index 100%
rename from error.c
rename to src/error.c
diff --git a/error.h b/src/error.h
similarity index 100%
rename from error.h
rename to src/error.h
diff --git a/image.c b/src/image.c
similarity index 87%
rename from image.c
rename to src/image.c
index 9cb404a..89d9386 100644
--- a/image.c
+++ b/src/image.c
@@ -5,11 +5,36 @@
#include <string.h>
#include <libswscale/swscale.h>
+// Changes for ffmpeg 3.0
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,0)
+# include <libavutil/imgutils.h>
+# define av_free_packet av_packet_unref
+# define avpicture_get_size(fmt,w,h) av_image_get_buffer_size(fmt,w,h,1)
+#endif
+
+// PIX_FMT was renamed to AV_PIX_FMT on this version
+#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,74,100)
+# define AVPixelFormat PixelFormat
+# define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
+# define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
+# define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
+# define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
+# define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
+# define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
+# define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
+# define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
+# define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
+#endif
+
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0)
-#define av_frame_alloc avcodec_alloc_frame
-#define av_frame_free av_freep
+# define av_frame_alloc avcodec_alloc_frame
+# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,59,100)
+# define av_frame_free av_freep
+# else
+# define av_frame_free avcodec_free_frame
+# endif
void av_frame_get_buffer(AVFrame *frame, int magic) { avpicture_alloc((AVPicture *)frame, frame->format, frame->width, frame->height); }
-void av_frame_copy(AVFrame *dst, AVFrame *src) { memcpy(dst->data[0], src->data[0], sizeof(uint8_t)*avpicture_get_size(PIX_FMT_RGB24, dst->width, dst->height)); }
+void av_frame_copy(AVFrame *dst, AVFrame *src) { memcpy(dst->data[0], src->data[0], sizeof(uint8_t)*avpicture_get_size(AV_PIX_FMT_RGB24, dst->width, dst->height)); }
#endif
#define MAX_FILTER_SIZE 256
@@ -25,7 +50,7 @@ image *image_init(const int width, const int height) {
i->frame = (AVFrame *) av_frame_alloc();
i->frame->width = width;
i->frame->height = height;
- i->frame->format = PIX_FMT_RGB24; // best choice?
+ i->frame->format = AV_PIX_FMT_RGB24; // best choice?
av_frame_get_buffer(i->frame, 16); // magic number?
return i;
}
@@ -240,7 +265,9 @@ int image_write_png(const image *i, const char *file_path) {
encoder_context = avcodec_alloc_context3(encoder);
encoder_context->width = i->frame->width;
encoder_context->height = i->frame->height;
- encoder_context->pix_fmt = PIX_FMT_RGB24;
+ encoder_context->pix_fmt = AV_PIX_FMT_RGB24;
+ encoder_context->time_base.num = 1;
+ encoder_context->time_base.den = 1;
if (avcodec_open2(encoder_context, encoder, NULL) < 0) {
error("Could not open output codec.");
return -1;
diff --git a/image.h b/src/image.h
similarity index 100%
rename from image.h
rename to src/image.h
diff --git a/main.c b/src/main.c
similarity index 81%
rename from main.c
rename to src/main.c
index b24f824..8a84419 100644
--- a/main.c
+++ b/src/main.c
@@ -1,4 +1,3 @@
-#include <pthread.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <stdarg.h>
@@ -69,9 +68,9 @@ void print_help(const poptContext popt, const int ret) {
printf("\n\
Examples:\n\
- nordlicht video.mp4 generate video.mp4.png of 1000 x 100 pixels size\n\
- nordlicht video.mp4 --style=vertical compress individual frames to columns\n\
- nordlicht video.mp4 -w 1920 -h 200 -o barcode.png override size and name of the output file\n");
+ nordlicht video.mp4 generate video.mp4.nordlicht.png of default size\n\
+ nordlicht video.mp4 -s vertical compress individual frames to columns\n\
+ nordlicht video.mp4 -w 1000 -h 1000 -o barcode.png override size and name of the output file\n");
exit(ret);
}
@@ -95,8 +94,8 @@ int main(const int argc, const char **argv) {
{"height", 'h', POPT_ARG_INT, &height, 0, "set the barcode's height; by default it's \"width/10\"", NULL},
{"output", 'o', POPT_ARG_STRING, &output_file, 0, "set output filename, the default is VIDEOFILE.png; when you specify an *.bgra file, you'll get a raw 32-bit BGRA file that is updated as the barcode is generated", "FILENAME"},
{"style", 's', POPT_ARG_STRING, &styles_string, 0, "default is 'horizontal', see \"Styles\" section below. You can specify more than one style, separated by '+', to get multiple tracks", "STYLE"},
- {"start", '\0', POPT_ARG_FLOAT, &start, 0, "specify where to start the barcode (in percent between 0 and 1)", NULL},
- {"end", '\0', POPT_ARG_FLOAT, &end, 0, "specify where to end the barcode (in percent between 0 and 1)", NULL},
+ {"start", '\0', POPT_ARG_FLOAT, &start, 0, "specify where to start the barcode (ratio between 0 and 1)", NULL},
+ {"end", '\0', POPT_ARG_FLOAT, &end, 0, "specify where to end the barcode (ratio between 0 and 1)", NULL},
{"quiet", 'q', 0, &quiet, 0, "don't show progress indicator", NULL},
{"help", '\0', 0, &help, 0, "display this help and exit", NULL},
{"version", '\0', 0, &version, 0, "output version information and exit", NULL},
@@ -106,13 +105,17 @@ int main(const int argc, const char **argv) {
const poptContext popt = poptGetContext(NULL, argc, argv, optionsTable, 0);
poptSetOtherOptionHelp(popt, "[OPTION]... VIDEOFILE\n\nOptions:");
- char c;
+ if (argc == 1) {
+ print_help(popt, 1);
+ }
+
+ int option;
- // The next line leaks 3 bytes, blame popt!
- while ((c = poptGetNextOpt(popt)) >= 0) { }
+ // The next line leaks 2 bytes, blame popt!
+ while ((option = poptGetNextOpt(popt)) >= 0) { }
- if (c < -1) {
- fprintf(stderr, "nordlicht: %s: %s\n", poptBadOption(popt, POPT_BADOPTION_NOALIAS), poptStrerror(c));
+ if (option < -1) {
+ fprintf(stderr, "nordlicht: %s: %s\n", poptBadOption(popt, POPT_BADOPTION_NOALIAS), poptStrerror(option));
return 1;
}
@@ -190,13 +193,13 @@ int main(const int argc, const char **argv) {
}
const char *ext = filename_ext(output_file);
- if (strcmp(ext, "png") == 0) {
- strategy = NORDLICHT_STRATEGY_FAST;
- } else if (strcmp(ext, "bgra") == 0) {
+ if (strcmp(ext, "bgra") == 0) {
strategy = NORDLICHT_STRATEGY_LIVE;
+ } else if (strcmp(ext, "png") == 0) {
+ strategy = NORDLICHT_STRATEGY_FAST;
} else {
- print_error("Unsupported file extension '%s'.", ext);
- exit(1);
+ strategy = NORDLICHT_STRATEGY_FAST;
+ fprintf(stderr, "nordlicht: Unsupported file extension '%s', will write a PNG.\n", ext);
}
// Interesting stuff begins here!
@@ -225,7 +228,10 @@ int main(const int argc, const char **argv) {
print_error("Could not open '%s'.", output_file);
exit(1);
}
- ftruncate(fd, nordlicht_buffer_size(n));
+ if (ftruncate(fd, nordlicht_buffer_size(n)) == -1) {
+ print_error("Could not truncate '%s'.", output_file);
+ exit(1);
+ }
data = (unsigned char *) mmap(NULL, nordlicht_buffer_size(n), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == (void *) -1) {
print_error("Could not mmap %d bytes.", nordlicht_buffer_size(n));
@@ -241,35 +247,35 @@ int main(const int argc, const char **argv) {
}
}
- pthread_t thread;
- pthread_create(&thread, NULL, (void*(*)(void*))nordlicht_generate, n);
-
- if (! quiet) {
- float progress = 0;
-
- printf("nordlicht: Building keyframe index... ");
- fflush(stdout);
- while (progress == 0) {
- progress = nordlicht_progress(n);
- nanosleep((const struct timespec[]){{0, 100000000L}}, NULL);
- }
- printf("done.\n");
-
- while (progress < 1) {
- progress = nordlicht_progress(n);
- printf("\rnordlicht: %02.0f%%", progress*100);
- fflush(stdout);
- nanosleep((const struct timespec[]){{0, 100000000L}}, NULL);
+ int phase = -1;
+ while(!nordlicht_done(n)) {
+ if (nordlicht_generate_step(n) == 0) {
+ if (! quiet) {
+ float progress = nordlicht_progress(n);
+ if (progress == 0) {
+ if (phase == -1) {
+ phase = 0;
+ printf("nordlicht: Building keyframe index... ");
+ fflush(stdout);
+ }
+ } else {
+ if (phase == 0) {
+ phase = 1;
+ printf("done.\n");
+ }
+ printf("\rnordlicht: %02.0f%%", progress*100);
+#ifdef DEBUG
+ printf("\n");
+#endif
+ fflush(stdout);
+ }
+ }
+ } else {
+ print_error(nordlicht_error());
+ exit(1);
}
}
- pthread_join(thread, NULL);
-
- if (nordlicht_error() != NULL) {
- print_error(nordlicht_error());
- exit(1);
- }
-
if (strategy != NORDLICHT_STRATEGY_LIVE) {
if (nordlicht_write(n, output_file) != 0) {
print_error(nordlicht_error());
diff --git a/src/nordlicht.c b/src/nordlicht.c
new file mode 100644
index 0000000..1199a45
--- /dev/null
+++ b/src/nordlicht.c
@@ -0,0 +1,385 @@
+#include "nordlicht.h"
+#include <string.h>
+#include "error.h"
+#include "source.h"
+
+#ifdef _WIN32
+#define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
+#endif
+
+typedef struct {
+ nordlicht_style style;
+ int height;
+} track;
+
+struct nordlicht {
+ int width, height;
+ char *filename;
+ track *tracks;
+ int num_tracks;
+ unsigned char *data;
+
+ int owns_data;
+ int modifiable;
+ nordlicht_strategy strategy;
+ source *source;
+
+ int current_pass;
+ int current_track;
+ int current_column;
+ int current_y_offset;
+ float progress;
+};
+
+NORDLICHT_API size_t nordlicht_buffer_size(const nordlicht *n) {
+ return n->width * n->height * 4;
+}
+
+NORDLICHT_API nordlicht* nordlicht_init(const char *filename, const int width, const int height) {
+ if (width < 1 || height < 1) {
+ error("Dimensions must be positive (got %dx%d)", width, height);
+ return NULL;
+ }
+ if (width > 100000 || height > 100000) {
+ error("Both dimensions may be at most 100000 (got %dx%d)", width, height);
+ return NULL;
+ }
+ nordlicht *n;
+ n = (nordlicht *) malloc(sizeof(nordlicht));
+
+ n->width = width;
+ n->height = height;
+
+ // prepend "file:" if filename contains a colon and is not a URL,
+ // otherwise ffmpeg will fail to open the file
+ if (filename && strstr(filename, ":") != 0 && strstr(filename, "://") == 0) {
+ size_t filename_len = strlen(filename);
+ n->filename = malloc(filename_len + 5 + 1);
+ strncpy(n->filename, "file:", 5);
+ strncpy(n->filename + 5, filename, filename_len);
+ n->filename[filename_len + 5] = '\0';
+ } else {
+ n->filename = (char *) filename;
+ }
+
+ n->data = (unsigned char *) calloc(nordlicht_buffer_size(n), 1);
+ if (n->data == 0) {
+ error("Not enough memory to allocate %d bytes", nordlicht_buffer_size(n));
+ return NULL;
+ }
+
+ n->owns_data = 1;
+
+ n->num_tracks = 1;
+ n->tracks = (track *) malloc(sizeof(track));
+ n->tracks[0].style = NORDLICHT_STYLE_HORIZONTAL;
+ n->tracks[0].height = n->height;
+
+ n->strategy = NORDLICHT_STRATEGY_FAST;
+ n->modifiable = 1;
+ n->source = source_init(n->filename);
+
+ n->current_pass = -1;
+ n->current_track = 0;
+ n->current_column = 0;
+ n->current_y_offset = 0;
+ n->progress = 0;
+
+ if (n->source == NULL) {
+ error("Could not open video file '%s'", filename);
+ free(n);
+ return NULL;
+ }
+
+ return n;
+}
+
+NORDLICHT_API void nordlicht_free(nordlicht *n) {
+ if (n->owns_data) {
+ free(n->data);
+ }
+ free(n->tracks);
+ source_free(n->source);
+ free(n);
+}
+
+NORDLICHT_API const char *nordlicht_error() {
+ return get_error();
+}
+
+NORDLICHT_API int nordlicht_set_start(nordlicht *n, const float start) {
+ if (! n->modifiable) {
+ return -1;
+ }
+
+ if (start < 0) {
+ error("'start' has to be greater than or equal to 0.");
+ return -1;
+ }
+
+ if (start >= source_end(n->source)) {
+ error("'start' has to be less than 'end'.");
+ return -1;
+ }
+
+ source_set_start(n->source, start);
+ return 0;
+}
+
+NORDLICHT_API int nordlicht_set_end(nordlicht *n, const float end) {
+ if (! n->modifiable) {
+ return -1;
+ }
+
+ if (end > 1) {
+ error("'end' has to less than or equal to 1.");
+ return -1;
+ }
+
+ if (source_start(n->source) >= end) {
+ error("'start' has to be less than 'end'.");
+ return -1;
+ }
+
+ source_set_end(n->source, end);
+ return 0;
+}
+
+NORDLICHT_API int nordlicht_set_style(nordlicht *n, const nordlicht_style style) {
+ if (! n->modifiable) {
+ return -1;
+ }
+
+ nordlicht_style styles[1] = {style};
+ return nordlicht_set_styles(n, styles, 1);
+}
+
+NORDLICHT_API int nordlicht_set_styles(nordlicht *n, const nordlicht_style *styles, const int num_tracks) {
+ if (! n->modifiable) {
+ return -1;
+ }
+
+ n->num_tracks = num_tracks;
+
+ if (n->num_tracks > n->height) {
+ error("Height of %d px is too low for %d styles", n->height, n->num_tracks);
+ return -1;
+ }
+
+ free(n->tracks);
+ n->tracks = (track *) malloc(n->num_tracks*sizeof(track));
+
+ int height_of_each_track = n->height/n->num_tracks;
+ int i;
+ for (i=0; i<num_tracks; i++) {
+ nordlicht_style s = styles[i];
+ if (s > NORDLICHT_STYLE_LAST-1) {
+ return -1;
+ }
+
+ n->tracks[i].style = s;
+ n->tracks[i].height = height_of_each_track;
+ }
+ n->tracks[0].height = n->height - (n->num_tracks-1)*height_of_each_track;
+
+ return 0;
+}
+
+NORDLICHT_API int nordlicht_set_strategy(nordlicht *n, const nordlicht_strategy s) {
+ if (! n->modifiable) {
+ return -1;
+ }
+ if (s > NORDLICHT_STRATEGY_LIVE) {
+ return -1;
+ }
+ n->strategy = s;
+ return 0;
+}
+
+NORDLICHT_API int nordlicht_generate_step(nordlicht *n) {
+ n->modifiable = 0;
+
+ if (nordlicht_done(n)) {
+ return 0;
+ } else if (n->current_pass == -1) {
+ // we don't have a (finished) keyframe index yet
+ if (source_build_keyframe_index_step(n->source, n->width) == 0) {
+ // keyframe index building is done
+ if (n->strategy == NORDLICHT_STRATEGY_LIVE || !source_has_index(n->source)) {
+ n->current_pass = 0;
+ } else {
+ n->current_pass = 1;
+ }
+ source_set_exact(n->source, n->current_pass);
+ }
+ } else {
+ image *frame;
+
+ if (n->tracks[n->current_track].style == NORDLICHT_STYLE_SPECTROGRAM) {
+ if (!source_has_audio(n->source)) {
+ error("File contains no audio, please select an appropriate style");
+ n->progress = 1;
+ return -1;
+ }
+ frame = source_get_audio_frame(n->source, 1.0*(n->current_column+0.5-COLUMN_PRECISION/2.0)/n->width,
+ 1.0*(n->current_column+0.5+COLUMN_PRECISION/2.0)/n->width);
+ } else {
+ if (!source_has_video(n->source)) {
+ error("File contains no video, please select an appropriate style");
+ n->progress = 1;
+ return -1;
+ }
+ frame = source_get_video_frame(n->source, 1.0*(n->current_column+0.5-COLUMN_PRECISION/2.0)/n->width,
+ 1.0*(n->current_column+0.5+COLUMN_PRECISION/2.0)/n->width);
+ }
+
+ if (frame != NULL) {
+ int thumbnail_width = 1.0*(image_width(frame)*n->tracks[n->current_track].height/image_height(frame));
+ image *column = NULL;
+ image *tmp = NULL;
+ switch (n->tracks[n->current_track].style) {
+ case NORDLICHT_STYLE_THUMBNAILS:
+ column = image_scale(frame, thumbnail_width, n->tracks[n->current_track].height);
+ break;
+ case NORDLICHT_STYLE_HORIZONTAL:
+ column = image_scale(frame, 1, n->tracks[n->current_track].height);
+ break;
+ case NORDLICHT_STYLE_VERTICAL:
+ tmp = image_scale(frame, n->tracks[n->current_track].height, 1);
+ column = image_flip(tmp);
+ image_free(tmp);
+ break;
+ case NORDLICHT_STYLE_SLITSCAN:
+ tmp = image_column(frame, 1.0*(n->current_column%thumbnail_width)/thumbnail_width);
+
+ column = image_scale(tmp, 1, n->tracks[n->current_track].height);
+ image_free(tmp);
+ break;
+ case NORDLICHT_STYLE_MIDDLECOLUMN:
+ tmp = image_column(frame, 0.5);
+ column = image_scale(tmp, 1, n->tracks[n->current_track].height);
+ image_free(tmp);
+ break;
+ case NORDLICHT_STYLE_SPECTROGRAM:
+ column = image_scale(frame, 1, n->tracks[n->current_track].height);
+ break;
+ default:
+ // cannot happen (TM)
+ return -1;
+ break;
+ }
+
+ image_to_bgra(n->data, n->width, n->height, column, n->current_column, n->current_y_offset);
+
+ n->current_column = n->current_column + image_width(column) - 1;
+ if (n->current_column >= n->width) {
+ n->current_column = n->width - 1;
+ }
+ n->progress = (n->current_track+1.0*n->current_column/n->width)/n->num_tracks;
+
+ image_free(column);
+ }
+
+ n->current_column++;
+ if (n->current_column == n->width) {
+ n->current_column = 0;
+ n->current_y_offset += n->tracks[n->current_track].height;
+ n->current_track++;
+ if (n->current_track == n->num_tracks) {
+ n->current_track = 0;
+ n->current_y_offset = 0;
+ n->current_pass++;
+ if (n->current_pass == 2 || !source_has_index(n->source)) {
+ // we're done :)
+ n->progress = 1.0;
+ n->current_pass = 2;
+ return 0;
+ } else {
+ source_set_exact(n->source, n->current_pass);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+NORDLICHT_API int nordlicht_generate(nordlicht *n) {
+ while(!nordlicht_done(n)) {
+ if (nordlicht_generate_step(n) != 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+NORDLICHT_API int nordlicht_write(const nordlicht *n, const char *filename) {
+ int code = 0;
+
+ if (filename == NULL) {
+ error("Output filename must not be NULL");
+ return -1;
+ }
+
+ if (strcmp(filename, "") == 0) {
+ error("Output filename must not be empty");
+ return -1;
+ }
+
+ char *realpath_output = realpath(filename, NULL);
+ if (realpath_output != NULL) {
+ // output file exists
+ char *realpath_input = realpath(n->filename, NULL);
+ if (realpath_input != NULL) {
+ // otherwise, input filename is probably a URL
+
+ if (strcmp(realpath_input, realpath_output) == 0) {
+ error("Will not overwrite input file");
+ code = -1;
+ }
+ free(realpath_input);
+ }
+ free(realpath_output);
+
+ if (code != 0) {
+ return code;
+ }
+ }
+
+ image *i = image_from_bgra(n->data, n->width, n->height);
+ if (image_write_png(i, filename) != 0) {
+ return -1;
+ }
+ image_free(i);
+
+ return code;
+}
+
+NORDLICHT_API int nordlicht_done(const nordlicht *n) {
+ return n->current_pass == 2;
+}
+
+NORDLICHT_API float nordlicht_progress(const nordlicht *n) {
+ return n->progress;
+}
+
+NORDLICHT_API const unsigned char* nordlicht_buffer(const nordlicht *n) {
+ return n->data;
+}
+
+NORDLICHT_API int nordlicht_set_buffer(nordlicht *n, unsigned char *data) {
+ if (! n->modifiable) {
+ return -1;
+ }
+
+ if (data == NULL) {
+ return -1;
+ }
+
+ if (n->owns_data) {
+ free(n->data);
+ }
+ n->owns_data = 0;
+ n->data = data;
+ return 0;
+}
diff --git a/nordlicht.h b/src/nordlicht.h
similarity index 58%
rename from nordlicht.h
rename to src/nordlicht.h
index e334f99..b5c6cde 100644
--- a/nordlicht.h
+++ b/src/nordlicht.h
@@ -2,7 +2,6 @@
#define INCLUDE_nordlicht_h__
#include <stdlib.h> // for size_t
-
#ifndef NORDLICHT_API
# ifdef _WIN32
# if defined(NORDLICHT_BUILD_SHARED) /* build dll */
@@ -28,13 +27,13 @@ extern "C" {
typedef struct nordlicht nordlicht;
typedef enum nordlicht_style {
- NORDLICHT_STYLE_THUMBNAILS,
- NORDLICHT_STYLE_HORIZONTAL, // compress frames to columns, "move to the right"
- NORDLICHT_STYLE_VERTICAL, // compress frames to rows, "move downwards"
+ NORDLICHT_STYLE_THUMBNAILS, // a row of thumbnails
+ NORDLICHT_STYLE_HORIZONTAL, // compress frames to columns
+ NORDLICHT_STYLE_VERTICAL, // compress frames to rows and rotate them counterclockwise
NORDLICHT_STYLE_SLITSCAN, // take single columns, while moving to the right (and wrapping to the left)
NORDLICHT_STYLE_MIDDLECOLUMN, // take the frames' middlemost column
NORDLICHT_STYLE_SPECTROGRAM, // spectrogram of the first audio track (not all sample formats are supported yet)
- NORDLICHT_STYLE_LAST
+ NORDLICHT_STYLE_LAST // just a marker so that we can count the number of available styles
} nordlicht_style;
typedef enum nordlicht_strategy {
@@ -42,26 +41,26 @@ typedef enum nordlicht_strategy {
NORDLICHT_STRATEGY_LIVE, // generate a fast approximation first, good for live display
} nordlicht_strategy;
-// Returns a description of the last error, or NULL if the was no error.
+// Returns a description of the last error, or NULL if there was no error.
NORDLICHT_API const char *nordlicht_error();
-// Allocate a new nordlicht of specific width and height, for a given video
-// file. When `live` is true, give a fast approximation before starting the
-// slow, exact generation. Use `nordlicht_free` to free the nordlicht again.
+// Allocate a new nordlicht of specified width and height, for a given video
+// file. Use `nordlicht_free` to free the nordlicht again.
// Returns NULL on errors.
NORDLICHT_API nordlicht* nordlicht_init(const char *filename, const int width, const int height);
// Free a nordlicht.
NORDLICHT_API void nordlicht_free(nordlicht *n);
-// Specify where to start the nordlicht, in percent between 0 and 1.
+// Specify where to start the nordlicht in the file, as a ratio between 0 and 1.
NORDLICHT_API int nordlicht_set_start(nordlicht *n, const float start);
-// Specify where to end the nordlicht, in percent between 0 and 1.
+// Specify where to end the nordlicht in the file, as a ratio between 0 and 1.
NORDLICHT_API int nordlicht_set_end(nordlicht *n, const float end);
-// Set the output style of the nordlicht. Default is NORDLICHT_STYLE_HORIZONTAL.
-// Returns 0 on success. To set multiple styles at once, use `nordlicht_set_styles`.
+// Set the output style of the nordlicht, see above. Default is NORDLICHT_STYLE_HORIZONTAL.
+// To set multiple styles at once, use `nordlicht_set_styles`.
+// Returns 0 on success.
NORDLICHT_API int nordlicht_set_style(nordlicht *n, const nordlicht_style style);
// Set multiple output styles, which will be displayed on top of each other.
@@ -69,26 +68,42 @@ NORDLICHT_API int nordlicht_set_style(nordlicht *n, const nordlicht_style style)
// Returns 0 on success.
NORDLICHT_API int nordlicht_set_styles(nordlicht *n, const nordlicht_style *styles, const int num_styles);
-// Set the generation strategy of the nordlicht. Default is NORDLICHT_STRATEGY_FAST.
-// Returns 0 on success. This function will be removed in the future.
+// Set the generation strategy of the nordlicht, see above. Default is NORDLICHT_STRATEGY_FAST.
+// Returns 0 on success.
NORDLICHT_API int nordlicht_set_strategy(nordlicht *n, const nordlicht_strategy strategy);
-// Returns a pointer to the nordlicht's internal buffer. You can use it to draw
+// Return a pointer to the nordlicht's internal buffer. You can use it to draw
// the barcode while it is generated. The pixel format is 32-bit BGRA.
NORDLICHT_API const unsigned char* nordlicht_buffer(const nordlicht *n);
// Replace the internal nordlicht's internal buffer. The data pointer is owned
-// by the caller and must be freed after `nordlicht_free`. Returns 0 on success.
+// by the caller and must be freed after `nordlicht_free`. You can use this to
+// render the nordlicht into caller-owned data structures, like mmap-ed files.
+// The pixel format is 32-bit BGRA.
+// Returns 0 on success.
NORDLICHT_API int nordlicht_set_buffer(nordlicht *n, unsigned char *data);
// Returns the size of this nordlicht's buffer in bytes.
NORDLICHT_API size_t nordlicht_buffer_size(const nordlicht *n);
-// Generate the nordlicht. Calling this will freeze the nordlicht:
-// "set" functions will fail. Returns 0 on success.
+// Generate the nordlicht in one pass. Use this function from a thread if you don't
+// want to block execution. Calling this will freeze the nordlicht: "set"
+// functions will fail.
+// Returns 0 on success.
NORDLICHT_API int nordlicht_generate(nordlicht *n);
-// Returns a value between 0 and 1 indicating how much of the nordlicht is done.
+// Do one step of generation, which will be as small as possible. Use this
+// if you don't want to start a seperate thread, but be aware that this
+// function might still take too long for real-time applications. Calling this
+// will freeze the nordlicht: "set" functions will fail.
+// Returns 0 on success.
+NORDLICHT_API int nordlicht_generate_step(nordlicht *n);
+
+// Returns 1 if the nordlicht has been completely generated, and 0 otherwise.
+NORDLICHT_API int nordlicht_done(const nordlicht *n);
+
+// Returns a value between 0 and 1 indicating how much of the nordlicht has been
+// generated.
NORDLICHT_API float nordlicht_progress(const nordlicht *n);
// Write the nordlicht to a PNG file. Returns 0 on success.
diff --git a/source.c b/src/source.c
similarity index 75%
rename from source.c
rename to src/source.c
index 182a536..344fff8 100644
--- a/source.c
+++ b/src/source.c
@@ -6,9 +6,34 @@
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
+// Changes for ffmpeg 3.0
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,0)
+# include <libavutil/imgutils.h>
+# define av_free_packet av_packet_unref
+# define avpicture_get_size(fmt,w,h) av_image_get_buffer_size(fmt,w,h,1)
+#endif
+
+// PIX_FMT was renamed to AV_PIX_FMT on this version
+#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,74,100)
+# define AVPixelFormat PixelFormat
+# define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
+# define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
+# define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
+# define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
+# define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
+# define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
+# define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
+# define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
+# define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
+#endif
+
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0)
-#define av_frame_alloc avcodec_alloc_frame
-#define av_frame_free av_freep
+# define av_frame_alloc avcodec_alloc_frame
+# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,59,100)
+# define av_frame_free av_freep
+# else
+# define av_frame_free avcodec_free_frame
+# endif
#endif
#define HEURISTIC_NUMBER_OF_FRAMES 1800 // how many frames will the heuristic look at?
@@ -46,6 +71,7 @@ struct source {
int *keyframes;
int number_of_keyframes;
int has_index;
+ int current_frame;
};
long packet_pts(stream *st, const AVPacket *packet) {
@@ -72,12 +98,18 @@ int grab_next_frame(source *s, stream *st) {
break;
default:
error("Stream has unknown media type.");
+#ifdef DEBUG
+ printf("Unknown media type?\n");
+#endif
return 1;
}
if (got_frame) {
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->frame->data[0] == 0) {
+#ifdef DEBUG
+ printf("grab_next_frame: st->frame->data[0] == 0\n");
+#endif
return 1;
}
}
@@ -89,16 +121,25 @@ int grab_next_frame(source *s, stream *st) {
} else {
av_free_packet(&s->packet);
st->current_frame = -1;
+#ifdef DEBUG
+ printf("grab_next_frame: av_read_frame() == 0\n");
+#endif
return 1;
}
}
av_free_packet(&s->packet);
st->current_frame = pts;
+#ifdef DEBUG
+ printf("grab_next_frame() grabbed frame %d\n", pts);
+#endif
return 0;
}
int seek_keyframe(source *s, stream *st, const long frame) {
+#ifdef DEBUG
+ printf("seek_keyframe(%ld)\n", frame);
+#endif
av_seek_frame(s->format, st->stream, frame/st->fps/st->time_base, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(st->codec);
return grab_next_frame(s, st) != 0;
@@ -109,50 +150,65 @@ int total_number_of_frames(const source *s, stream *st) {
return st->fps*duration_sec;
}
-void source_build_keyframe_index(source *s, const int width) {
- if (! s->video) {
- return;
- }
-
- s->keyframes = (int *) malloc(sizeof(long)*60*60*3); // TODO: dynamic datastructure!
- s->number_of_keyframes = 0;
+// Returns 0 if done
+int source_build_keyframe_index_step(source *s, const int width) {
+ if (s->keyframes == NULL) {
+ if (! s->video) {
+ return 0;
+ }
+ s->keyframes = (int *) malloc(sizeof(long)*60*60*3); // TODO: dynamic datastructure!
+ s->keyframes[0] = 0;
+ s->number_of_keyframes = 1;
- int frame = 0;
+ s->current_frame = 0;
- s->has_index = 0;
- s->exact = 1;
- seek_keyframe(s, s->video, 0);
+ s->has_index = 0;
+ s->exact = 1;
+ }
- while (av_read_frame(s->format, &s->packet) >= 0) {
+ if (av_read_frame(s->format, &s->packet) >= 0) {
if (s->packet.stream_index == s->video->stream) {
if (!!(s->packet.flags & AV_PKT_FLAG_KEY)) {
s->number_of_keyframes++;
long pts = packet_pts(s->video, &s->packet);
if (pts < 1 && s->number_of_keyframes > 0) {
- pts = frame;
+ pts = s->current_frame;
}
+#ifdef DEBUG
+ printf("Found a keyframe: %ld\n", pts);
+#endif
s->keyframes[s->number_of_keyframes] = pts;
}
- if (frame == HEURISTIC_NUMBER_OF_FRAMES) {
+ if (s->current_frame == HEURISTIC_NUMBER_OF_FRAMES) {
const float density = 1.0*s->number_of_keyframes/HEURISTIC_NUMBER_OF_FRAMES;
const float required_density = 1.0*HEURISTIC_KEYFRAME_FACTOR/COLUMN_PRECISION*width/total_number_of_frames(s, s->video)/(s->end-s->start);
if (density > required_density) {
// The keyframe density in the first `HEURISTIC_NUMBER_OF_FRAMES`
// frames is HEURISTIC_KEYFRAME_FACTOR times higher than
- // the density we need overall.
- s->exact = 0;
+ // the density we need overall. This means that we can abort build
+ // the keyframe index and just seek inexactly to get good results.
av_free_packet(&s->packet);
- return;
+#ifdef DEBUG
+ printf("Keyframe indexing not necessary. Aborting.\n");
+#endif
+ return 0;
}
}
- frame++;
+ s->current_frame++;
}
av_free_packet(&s->packet);
+ return 1;
+ } else {
+ // we read through the whole file
+ av_free_packet(&s->packet);
+ s->has_index = 1;
+#ifdef DEBUG
+ printf("Keyframe indexing complete.\n");
+#endif
+ return 0;
}
- av_free_packet(&s->packet);
- s->has_index = 1;
}
stream* stream_init(source *s, enum AVMediaType type) {
@@ -212,6 +268,9 @@ source* source_init(const char *filename) {
av_log_set_level(AV_LOG_FATAL);
av_register_all();
+ if (strstr(filename, "://") != 0) {
+ avformat_network_init();
+ }
source *s;
s = (source *) malloc(sizeof(source));
@@ -226,6 +285,7 @@ source* source_init(const char *filename) {
}
if (avformat_find_stream_info(s->format, NULL) < 0) {
avformat_close_input(&s->format);
+ free(s);
return NULL;
}
@@ -235,6 +295,7 @@ source* source_init(const char *filename) {
if (!s->video && !s->audio) {
error("File contains neither video nor audio");
avformat_close_input(&s->format);
+ free(s);
return NULL;
}
@@ -244,13 +305,17 @@ source* source_init(const char *filename) {
s->scaleframe = av_frame_alloc();
s->scaleframe->width = s->video->frame->width;
s->scaleframe->height = s->video->frame->height;
- s->scaleframe->format = PIX_FMT_RGB24;
+ s->scaleframe->format = AV_PIX_FMT_RGB24;
- s->buffer = (uint8_t *)av_malloc(sizeof(uint8_t)*avpicture_get_size(PIX_FMT_RGB24, s->scaleframe->width, s->scaleframe->height));
- avpicture_fill((AVPicture *)s->scaleframe, s->buffer, PIX_FMT_RGB24, s->video->frame->width, s->video->frame->height);
+ s->buffer = (uint8_t *)av_malloc(sizeof(uint8_t)*avpicture_get_size(s->scaleframe->format, s->scaleframe->width, s->scaleframe->height));
+ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,0)
+ av_image_fill_arrays(s->scaleframe->data, s->scaleframe->linesize, s->buffer, s->scaleframe->format, s->video->frame->width, s->video->frame->height, 1);
+ #else
+ avpicture_fill((AVPicture *)s->scaleframe, s->buffer, s->scaleframe->format, s->video->frame->width, s->video->frame->height);
+ #endif
s->sws_context = sws_getCachedContext(NULL, s->video->frame->width, s->video->frame->height, s->video->frame->format,
- s->scaleframe->width, s->scaleframe->height, PIX_FMT_RGB24, SWS_AREA, NULL, NULL, NULL);
+ s->scaleframe->width, s->scaleframe->height, s->scaleframe->format, SWS_AREA, NULL, NULL, NULL);
}
s->keyframes = NULL;
@@ -279,29 +344,44 @@ long preceding_keyframe(source *s, const long frame_nr) {
best_keyframe = s->keyframes[i];
}
}
+#ifdef DEBUG
+ printf("preceding_keyframe(%ld) -> %ld\n", frame_nr, best_keyframe);
+#endif
return best_keyframe;
}
int seek(source *s, stream *st, const long min_frame_nr, const long max_frame_nr) {
+#ifdef DEBUG
+ printf("seek(%ld, %ld)\n", min_frame_nr, max_frame_nr);
+#endif
if (s->exact && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
long keyframe = preceding_keyframe(s, max_frame_nr);
- if (keyframe > st->current_frame) {
+ if (keyframe > st->current_frame || max_frame_nr < st->current_frame) {
if (seek_keyframe(s, st, keyframe) != 0) {
+#ifdef DEBUG
+ printf("seek_keyframe() == 0\n");
+#endif
return 1;
}
}
while (st->current_frame < min_frame_nr) {
if (st->current_frame > max_frame_nr) {
- error("Target frame is in the past. This shoudn't happen. Please file a bug.");
+ error("Target frame is in the past. This shouldn't happen. Please file a bug.");
}
if (grab_next_frame(s, st) != 0) {
+#ifdef DEBUG
+ printf("grab_next_frame() == 0\n");
+#endif
return 1;
}
}
} else {
if (seek_keyframe(s, st, (min_frame_nr+max_frame_nr)/2) != 0) {
+#ifdef DEBUG
+ printf("seek_keyframe() == 0\n");
+#endif
return 1;
}
}
@@ -401,8 +481,8 @@ image* source_get_audio_frame(source *s, const double min_percent, const double
return get_frame(s, s->audio, min_percent, max_percent);
}
-int source_exact(const source *s) {
- return s->exact;
+int source_has_index(const source *s) {
+ return s->has_index;
}
void source_set_exact(source *s, const int exact) {
diff --git a/source.h b/src/source.h
similarity index 88%
rename from source.h
rename to src/source.h
index bbf4506..9d7377d 100644
--- a/source.h
+++ b/src/source.h
@@ -18,9 +18,9 @@ image* source_get_video_frame(source *s, const double min_percent, const double
image* source_get_audio_frame(source *s, const double min_percent, const double max_percent);
-void source_build_keyframe_index(source *s, const int width);
+int source_build_keyframe_index_step(source *s, const int width);
-int source_exact(const source *s);
+int source_has_index(const source *s);
void source_set_exact(source *s, const int exact);
diff --git a/testsuite.c b/src/testsuite.c
similarity index 70%
rename from testsuite.c
rename to src/testsuite.c
index 8902723..a0d1f23 100644
--- a/testsuite.c
+++ b/src/testsuite.c
@@ -42,7 +42,10 @@ CHEAT_TEST(invalid_input_file,
cheat_null(nordlicht_init("\0", 100, 100));
cheat_null(nordlicht_init(".", 100, 100));
cheat_null(nordlicht_init("..", 100, 100));
+ cheat_null(nordlicht_init("/", 100, 100));
+ cheat_null(nordlicht_init("\\", 100, 100));
cheat_null(nordlicht_init("nonexistent_file.123", 100, 100));
+ cheat_null(nordlicht_init("video.mp4.", 100, 100));
)
CHEAT_TEST(invalid_size,
@@ -54,54 +57,69 @@ CHEAT_TEST(invalid_size,
cheat_null(nordlicht_init("video.mp4", -100, 100));
cheat_null(nordlicht_init("video.mp4", 100, -100));
cheat_null(nordlicht_init("video.mp4", INT_MIN, INT_MIN));
+ cheat_null(nordlicht_init("video.mp4", 100001, 100001));
+ cheat_null(nordlicht_init("video.mp4", 100001, 1));
+ cheat_null(nordlicht_init("video.mp4", 1, 100001));
)
CHEAT_TEST(valid_size,
cheat_not_null(nordlicht_init("video.mp4", 1, 1));
- cheat_not_null(nordlicht_init("video.mp4", INT_MAX, INT_MAX));
+ cheat_not_null(nordlicht_init("video.mp4", 100, 100));
+ cheat_not_null(nordlicht_init("video.mp4", 100000, 100000));
+ cheat_not_null(nordlicht_init("video.mp4", 100000, 1));
+ cheat_not_null(nordlicht_init("video.mp4", 1, 100000));
)
CHEAT_TEST(invalid_output,
- n = nordlicht_init("video.mp4", 1, 100);
+ n = nordlicht_init("video.mp4", 100, 100);
cheat_not_null(n);
cheat_fail(nordlicht_write(n, NULL));
cheat_fail(nordlicht_write(n, ""));
cheat_fail(nordlicht_write(n, "\0"));
cheat_fail(nordlicht_write(n, "."));
cheat_fail(nordlicht_write(n, ".."));
+ cheat_fail(nordlicht_write(n, "/"));
cheat_fail(nordlicht_write(n, "video.mp4"));
)
CHEAT_TEST(style,
- n = nordlicht_init("video.mp4", 1, 100);
+ n = nordlicht_init("video.mp4", 100, 100);
cheat_not_null(n);
cheat_ok(nordlicht_set_style(n, NORDLICHT_STYLE_HORIZONTAL));
cheat_ok(nordlicht_set_style(n, NORDLICHT_STYLE_VERTICAL));
- cheat_fail(nordlicht_set_style(n, 10000000));
+ cheat_ok(nordlicht_set_style(n, NORDLICHT_STYLE_LAST-1));
+ cheat_fail(nordlicht_set_style(n, -1));
+ cheat_fail(nordlicht_set_style(n, INT_MAX));
+ cheat_fail(nordlicht_set_style(n, INT_MIN));
)
CHEAT_TEST(styles,
- n = nordlicht_init("video.mp4", 1, 100);
+ n = nordlicht_init("video.mp4", 100, 2);
cheat_not_null(n);
- nordlicht_style styles[2];
+ nordlicht_style styles[3];
styles[0] = NORDLICHT_STYLE_HORIZONTAL;
styles[1] = NORDLICHT_STYLE_VERTICAL;
cheat_ok(nordlicht_set_styles(n, styles, 2));
- styles[0] = NORDLICHT_STYLE_THUMBNAILS;
+ styles[0] = NORDLICHT_STYLE_LAST-1;
cheat_ok(nordlicht_set_styles(n, styles, 2));
- styles[0] = 10000000;
+ styles[0] = INT_MAX;
cheat_fail(nordlicht_set_styles(n, styles, 2));
+ styles[0] = INT_MIN;
+ cheat_fail(nordlicht_set_styles(n, styles, 2));
+ styles[2] = NORDLICHT_STYLE_HORIZONTAL;
+ cheat_fail(nordlicht_set_styles(n, styles, 3));
)
CHEAT_TEST(strategy,
- n = nordlicht_init("video.mp4", 1, 100);
+ n = nordlicht_init("video.mp4", 100, 100);
cheat_not_null(n);
cheat_ok(nordlicht_set_strategy(n, NORDLICHT_STRATEGY_FAST));
cheat_ok(nordlicht_set_strategy(n, NORDLICHT_STRATEGY_LIVE));
cheat_fail(nordlicht_set_strategy(n, 2));
- cheat_fail(nordlicht_set_strategy(n, 1000000));
cheat_fail(nordlicht_set_strategy(n, -1));
+ cheat_fail(nordlicht_set_strategy(n, INT_MAX));
+ cheat_fail(nordlicht_set_strategy(n, INT_MIN));
)
CHEAT_TEST(buffer,
@@ -122,6 +140,22 @@ CHEAT_TEST(buffer,
free(buffer2);
)
+CHEAT_TEST(generate_step,
+ n = nordlicht_init("video.mp4", 1, 100);
+ cheat_assert(0 == nordlicht_progress(n));
+ cheat_assert(!nordlicht_done(n));
+ cheat_ok(nordlicht_set_start(n, 0.5));
+ cheat_assert(0 == nordlicht_generate_step(n));
+ cheat_assert(!nordlicht_done(n));
+ cheat_fail(nordlicht_set_start(n, 0.5));
+ cheat_assert(0 == nordlicht_generate_step(n));
+ cheat_assert(0 == nordlicht_generate_step(n));
+ cheat_ok(nordlicht_generate(n));
+ cheat_assert(nordlicht_done(n));
+ cheat_assert(0 == nordlicht_generate_step(n));
+ cheat_assert(nordlicht_done(n));
+)
+
CHEAT_TEST(complete_run,
unsigned char *buffer2 = NULL;
n = nordlicht_init("video.mp4", 1, 100);
@@ -139,13 +173,15 @@ CHEAT_TEST(tool_argument_parsing,
cheat_ok(tool("--version"));
cheat_fail(tool(""));
cheat_fail(tool("--fubar"));
+ cheat_fail(tool("-1"));
cheat_fail(tool("one.mp4 two.mp4"));
cheat_fail(tool("does_not_exist.mp4"));
)
CHEAT_TEST(tool_size,
cheat_ok(tool("video.mp4 -w 1 -h 1"));
- cheat_ok(tool("video.mp4 -w 1 -h 10000"));
+ cheat_ok(tool("video.mp4 -w 1 -h 100000"));
+ cheat_fail(tool("video.mp4 -w 1 -h 100001"));
cheat_fail(tool("video.mp4 -w huuuge"));
cheat_fail(tool("video.mp4 -w 0"));
cheat_fail(tool("video.mp4 -h 0"));
@@ -166,9 +202,13 @@ CHEAT_TEST(tool_output,
cheat_assert(-1 != access("ünîç⌀də.png", F_OK));
cheat_fail(tool("video.mp4 -o video.mp4"));
cheat_fail(tool("video.mp4 -o ''"));
+ cheat_fail(tool("video.mp4 -o ."));
+ cheat_fail(tool("video.mp4 -o .."));
+ cheat_fail(tool("video.mp4 -o /"));
cheat_ok(tool("video.mp4 -w 1 -o barcode.bgra"));
- cheat_fail(tool("video.mp4 -o barcode.abc"));
- cheat_fail(tool("video.mp4 -o barcode"));
+ // fall back to PNG in these two cases:
+ cheat_ok(tool("video.mp4 -w 1 -o barcode.abc"));
+ cheat_ok(tool("video.mp4 -w 1 -o barcode"));
)
CHEAT_TEST(tool_style,
@@ -179,9 +219,11 @@ CHEAT_TEST(tool_style,
cheat_ok(tool("video.mp4 -h 2 -s horizontal+vertical"));
cheat_fail(tool("video.mp4 -s horizontal+"));
cheat_fail(tool("video.mp4 -s +"));
+ cheat_fail(tool("video.mp4 -s ++"));
cheat_fail(tool("video.mp4 -s horizontal++horizontal"));
cheat_fail(tool("video.mp4 -s nope"));
cheat_fail(tool("video.mp4 -s ''"));
+ cheat_fail(tool("video.mp4 -s"));
)
CHEAT_TEST(tool_region,
@@ -196,4 +238,5 @@ CHEAT_TEST(tool_region,
cheat_fail(tool("video.mp4 -w 1 --end=0"));
cheat_fail(tool("video.mp4 -w 1 --end=2"));
cheat_fail(tool("video.mp4 -w 1 --start=0.2 --end=0.1"));
+ cheat_fail(tool("video.mp4 -w 1 --start=0.5 --end=0.5"));
)
diff --git a/version.h.in b/src/version.h.in
similarity index 100%
rename from version.h.in
rename to src/version.h.in
diff --git a/utils/mpv-nordlicht.lua b/utils/mpv-nordlicht.lua
index 698697c..4d06d2b 100644
--- a/utils/mpv-nordlicht.lua
+++ b/utils/mpv-nordlicht.lua
@@ -25,6 +25,10 @@ end
function shutdown()
off()
kill()
+
+ if buffer and buffer:len() > 0 then
+ os.remove(buffer)
+ end
end
function kill()
@@ -41,7 +45,13 @@ function new_file()
video = mp.get_property("path")
nordlicht = video..".nordlicht.png"
- buffer = "/tmp/nordlicht.bgra"
+
+ if buffer and buffer:len() > 0 then
+ os.remove(buffer)
+ end
+ local tmpbuffer = os.tmpname()
+ buffer = tmpbuffer .. ".nordlicht.mpv.bgra"
+ os.rename(tmpbuffer, buffer)
local f = io.open(nordlicht, "r")
if f ~= nil then
@@ -49,6 +59,7 @@ function new_file()
io.close(f)
local cmd = {"convert", nordlicht, "-depth", "8", "-resize", width.."x"..height.."!", buffer}
utils.subprocess({args=cmd})
+ utils.subprocess({args={"chmod", "600", buffer}})
if was_on then
on()
@@ -114,7 +125,14 @@ end
function regenerate()
local was_on = is_on
off()
- local cmd = "(nice nordlicht -s "..styles.." \""..video.."\" -o "..buffer.." -w "..width.." -h "..height.." && convert -depth 8 -size "..width.."x"..height.." "..buffer.." \""..nordlicht.."\") &"
+
+ local safe_buffer = buffer:gsub('"', '\\"')
+ local safe_video = video:gsub('"', '\\"')
+ local safe_nordlicht = nordlicht:gsub('"', '\\"')
+ local cmd = ('(nice nordlicht -s %s "%s" -o "%s" -w %d -h %d' ..
+ ' && convert -depth 8 -size %dx%d "%s" "%s") &')
+ :format(styles, safe_video, safe_buffer, width, height,
+ width, height, safe_buffer, safe_nordlicht)
os.execute(cmd)
if was_on then
on()
@@ -122,11 +140,14 @@ function regenerate()
end
function jump(e)
- local mouseX, mouseY = mp.get_mouse_pos()
- local osdX, osdY = mp.get_osd_resolution()
- mouseX = 100.0*mouseX/osdX
+ local mouse_x, mouse_y = mp.get_mouse_pos()
+ local osd_x, osd_y = mp.get_osd_resolution()
+ local screen_y = mp.get_property("osd-height")
+ local absolute_mouse_y = 1.0*mouse_y/osd_y*screen_y
- mp.commandv("seek", mouseX, "absolute-percent", "exact")
+ if absolute_mouse_y <= mh+height and is_on then
+ mp.commandv("seek", 100.0*mouse_x/osd_x, "absolute-percent", "exact")
+ end
end
-- wait until the osd-width is > 0, then init
diff --git a/utils/nordlicht.thumbnailer b/utils/nordlicht.thumbnailer
new file mode 100644
index 0000000..35a1086
--- /dev/null
+++ b/utils/nordlicht.thumbnailer
@@ -0,0 +1,4 @@
+[Thumbnailer Entry]
+TryExec=nordlicht
+Exec=nordlicht --quiet %i --output %o --width %s --height %s
+MimeType=video/jpeg;video/mp4;video/mpeg;video/quicktime;video/x-ms-asf;video/x-ms-wm;video/x-ms-wmv;video/x-msvideo;video/x-flv;video/x-matroska;video/webm;
--
nordlicht packaging
More information about the pkg-multimedia-commits
mailing list