Skip to content
Merged
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
8 changes: 2 additions & 6 deletions av/datasets.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import errno
import logging
import os
import shutil
import sys
from collections.abc import Iterator
from urllib.request import urlopen
Expand Down Expand Up @@ -85,12 +86,7 @@ def cached_download(url: str, name: str) -> str:

tmp_path = path + ".tmp"
with open(tmp_path, "wb") as fh:
while True:
chunk = response.read(8196)
if chunk:
fh.write(chunk)
else:
break
shutil.copyfileobj(response, fh)

os.rename(tmp_path, path)

Expand Down
92 changes: 17 additions & 75 deletions av/error.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import enum
import errno
import os
import sys
Expand All @@ -19,7 +20,6 @@
"HTTPClientError",
"UndefinedError",
]
sentinel = cython.declare(object, object())


@cython.ccall
Expand Down Expand Up @@ -169,86 +169,28 @@ class HTTPClientError(FFmpegError):
# fmt: on


class EnumType(type):
def __new__(mcl, name, bases, attrs, *args):
# Just adapting the method signature.
return super().__new__(mcl, name, bases, attrs)

def __init__(self, name, bases, attrs, items):
self._by_name = {}
self._by_value = {}
self._all = []

for spec in items:
self._create(*spec)

def _create(self, name, value, doc=None, by_value_only=False):
# We only have one instance per value.
try:
item = self._by_value[value]
except KeyError:
item = self(sentinel, name, value, doc)
self._by_value[value] = item

return item

def __len__(self):
return len(self._all)

def __iter__(self):
return iter(self._all)


@cython.cclass
class EnumItem:
"""An enumeration of FFmpeg's error types.

.. attribute:: tag

The FFmpeg byte tag for the error.

.. attribute:: strerror

The error message that would be returned.
"""

name = cython.declare(str, visibility="readonly")
value = cython.declare(cython.int, visibility="readonly")

def __cinit__(self, sentinel_, name: str, value: cython.int, doc=None):
if sentinel_ is not sentinel:
raise RuntimeError(f"Cannot instantiate {self.__class__.__name__}.")

self.name = name
self.value = value
self.__doc__ = doc

def __repr__(self):
return f"<{self.__class__.__module__}.{self.__class__.__name__}:{self.name}(0x{self.value:x})>"

def __str__(self):
return self.name
ErrorType = enum.IntEnum(
"ErrorType",
[(name, value) for name, value, *_ in _ffmpeg_specs],
module=__name__,
)
ErrorType.__doc__ = "An enumeration of FFmpeg's error types."

def __int__(self):
return self.value

@property
def tag(self):
return code_to_tag(self.value)
def _error_type_tag(self) -> bytes:
"""The FFmpeg byte tag for the error."""
return code_to_tag(self.value)


ErrorType = EnumType(
"ErrorType", (EnumItem,), {"__module__": __name__}, [x[:2] for x in _ffmpeg_specs]
)
def _error_type_strerror(self) -> str:
"""The error message that would be returned."""
if self.value == c_PYAV_STASHED_ERROR:
return PYAV_STASHED_ERROR_message
return lib.av_err2str(-self.value)


for enum in ErrorType:
# Mimick the errno module.
globals()[enum.name] = enum
if enum.value == c_PYAV_STASHED_ERROR:
enum.strerror = PYAV_STASHED_ERROR_message
else:
enum.strerror = lib.av_err2str(-enum.value)
ErrorType.tag = property(_error_type_tag)
ErrorType.strerror = property(_error_type_strerror)

classes: dict = {}

Expand Down
83 changes: 10 additions & 73 deletions av/filter/loudnorm_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,23 @@
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <stdarg.h>
#include <string.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif

#ifdef _WIN32
static CRITICAL_SECTION json_mutex;
static CONDITION_VARIABLE json_cond;
static int mutex_initialized = 0;
#else
static pthread_mutex_t json_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t json_cond = PTHREAD_COND_INITIALIZER;
#endif

static char json_buffer[2048] = {0};
static int json_captured = 0;

// Custom logging callback
// Custom logging callback. The loudnorm filter prints its stats as a JSON line.
static void logging_callback(void *ptr, int level, const char *fmt, va_list vl) {
char line[2048];
vsnprintf(line, sizeof(line), fmt, vl);

const char *json_start = strstr(line, "{");
if (json_start) {
#ifdef _WIN32
EnterCriticalSection(&json_mutex);
#else
pthread_mutex_lock(&json_mutex);
#endif

size_t len = strnlen(json_start, sizeof(json_buffer) - 1);
memcpy(json_buffer, json_start, len);
json_buffer[len] = '\0';
json_captured = 1;

#ifdef _WIN32
WakeConditionVariable(&json_cond);
LeaveCriticalSection(&json_mutex);
#else
pthread_cond_signal(&json_cond);
pthread_mutex_unlock(&json_mutex);
#endif
}
}

Expand All @@ -74,15 +46,6 @@ char* loudnorm_get_stats(
json_captured = 0; // Reset the captured flag
memset(json_buffer, 0, sizeof(json_buffer)); // Clear the buffer

#ifdef _WIN32
// Initialize synchronization objects if needed
if (!mutex_initialized) {
InitializeCriticalSection(&json_mutex);
InitializeConditionVariable(&json_cond);
mutex_initialized = 1;
}
#endif

av_log_set_callback(logging_callback);

AVFilterGraph *filter_graph = NULL;
Expand Down Expand Up @@ -181,47 +144,21 @@ char* loudnorm_get_stats(

end:
// Freeing the graph uninits the loudnorm filter, which is what makes it
// emit its JSON stats through our log callback. Safe to call on NULL.
// emit its JSON stats through our log callback. This happens synchronously
// on this thread, so json_buffer is populated by the time the call returns.
avfilter_graph_free(&filter_graph);
avcodec_free_context(&codec_ctx);
avformat_close_input(&fmt_ctx);
av_frame_free(&filt_frame);
av_frame_free(&frame);
av_packet_free(&packet);

// If the graph never configured we produced no stats; don't block waiting
// for JSON that will never arrive. Leave result NULL so the caller raises.
if (graph_configured) {
#ifdef _WIN32
EnterCriticalSection(&json_mutex);
while (!json_captured) {
if (!SleepConditionVariableCS(&json_cond, &json_mutex, 5000)) { // 5 second timeout
fprintf(stderr, "Timeout waiting for JSON data\n");
break;
}
}
if (json_captured) {
result = _strdup(json_buffer); // Use _strdup on Windows
}
LeaveCriticalSection(&json_mutex);
#else
struct timespec timeout;
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 5; // 5 second timeout

pthread_mutex_lock(&json_mutex);
while (json_captured == 0) {
int ret = pthread_cond_timedwait(&json_cond, &json_mutex, &timeout);
if (ret == ETIMEDOUT) {
fprintf(stderr, "Timeout waiting for JSON data\n");
break;
}
}
if (json_captured) {
result = strdup(json_buffer);
}
pthread_mutex_unlock(&json_mutex);
#endif
if (graph_configured && json_captured) {
#ifdef _WIN32
result = _strdup(json_buffer);
#else
result = strdup(json_buffer);
#endif
}

av_log_set_callback(av_log_default_callback);
Expand Down
Loading