65static std::unique_ptr<Cache>
cache;
101 bool reported =
false;
104 if (fuse_interrupted())
126 Logging::trace(cache_entry->
virtname(),
"Cache miss at offset %1 with length %2 for segment no. %3.", offset, len, segment_no);
141 Logging::trace(cache_entry->
virtname(),
"Cache hit at offset %1 with length %2 for segment no. %3.", offset, len, segment_no);
147 catch (
bool _success)
195 cache_entry->
flush();
222 "Re-opening incomplete cache to generate missing %1 no. %2.",
248 if (cache_entry ==
nullptr || cache_entry->
m_buffer ==
nullptr)
254 const std::string cachefile = cache_entry->
m_buffer->cachefile(segment_no);
259 "Cached %1 no. %2 is marked available, but cache file '%3' is missing or empty. Recreating it.",
267 "Cached file is marked available, but cache file '%1' is missing or empty. Recreating it.",
271 cache_entry->
m_buffer->invalidate_segment(segment_no);
285 if (item_no && current_format !=
nullptr && current_format->
is_multiformat())
308 if (cache_entry ==
nullptr || cache_entry->
m_buffer ==
nullptr)
314 const size_t end = offset + len;
315 const bool logically_available =
317 (segment_no && cache_entry->
m_buffer->is_segment_finished(segment_no)) ||
318 (cache_entry->
m_buffer->tell(segment_no) >= end);
320 if (!logically_available)
325 if (cache_entry->
m_buffer->cachefile_valid(segment_no))
333 segment_no ?
"segment" :
"file");
347 *path =
"/var/cache";
352 if (
const char* cache_home = std::getenv(
"XDG_CACHE_HOME"))
372 if (
cache ==
nullptr)
375 cache = std::make_unique<Cache>();
376 if (
cache ==
nullptr)
378 Logging::error(
nullptr,
"Unable to create new media file cache. Out of memory.");
379 std::fprintf(stderr,
"ERROR: Creating new media file cache. Out of memory.\n");
383 if (!
cache->load_index())
385 std::fprintf(stderr,
"ERROR: Creating media file cache failed.\n");
394 if (
cache !=
nullptr)
405 if (cache_entry ==
nullptr)
412 if (!encoded_filesize)
418 if (encoded_filesize)
429bool transcoder_set_filesize(
LPVIRTUALFILE virtualfile, int64_t duration,
BITRATE audio_bit_rate,
int channels,
int sample_rate, AVSampleFormat sample_format,
BITRATE video_bit_rate,
int width,
int height,
bool interleaved,
const AVRational &framerate)
432 if (cache_entry ==
nullptr)
439 if (current_format ==
nullptr)
472 bool success =
false;
476 if (cache_entry !=
nullptr)
506 if (cache_entry ==
nullptr)
511 bool expected_is_decoding =
false;
512 if (!cache_entry->
m_is_decoding.compare_exchange_strong(expected_is_decoding,
true))
514 std::unique_lock<std::recursive_mutex> lock_active_mutex(cache_entry->
m_active_mutex, std::try_to_lock);
515 if (!lock_active_mutex.owns_lock())
522 "Transcoder thread was marked as running, but no active worker owns the cache entry. Resetting decoding state and starting a new worker.");
525 expected_is_decoding =
false;
526 if (!cache_entry->
m_is_decoding.compare_exchange_strong(expected_is_decoding,
true))
534 if (current_format !=
nullptr &&
558 std::shared_ptr<THREAD_DATA> thread_data = std::make_shared<THREAD_DATA>();
560 thread_data->m_initialised =
false;
561 thread_data->m_cache_entry = cache_entry;
562 thread_data->m_thread_running_lock_guard =
false;
565 std::unique_lock<std::mutex> lock_thread_running_mutex(thread_data->m_thread_running_mutex);
570 while (!thread_data->m_thread_running_lock_guard)
572 thread_data->m_thread_running_cond.wait(lock_thread_running_mutex);
594 if (cache_entry ==
nullptr)
606 const bool create_cache = begin_transcode ||
607 (current_format !=
nullptr &&
611 if (!cache_entry->
openio(create_cache))
613 throw static_cast<int>(errno);
619 cache_entry->
clear();
623 cache_entry->
clear();
654 throw static_cast<int>(errno);
658 else if (begin_transcode)
670 cache_entry =
nullptr;
683 Logging::trace(cache_entry->
virtname(),
"Reading %1 bytes from offset %2 to %3.", len, offset, len + offset);
687 Logging::trace(cache_entry->
virtname(),
"Reading %1 bytes from offset %2 to %3 for segment no. %4.", len, offset, len + offset, segment_no);
698 const bool segment_logically_complete = segment_no &&
700 bool segment_complete = segment_logically_complete;
701 bool repair_requested =
false;
703 if (segment_logically_complete && !cache_entry->
m_buffer->cachefile_valid(segment_no))
706 segment_complete =
false;
707 repair_requested =
true;
710 if (!segment_no && cache_entry->
is_finished() && !cache_entry->
m_buffer->cachefile_valid(0))
713 repair_requested =
true;
716 const bool segment_missing = segment_no && !segment_complete;
753 const uint32_t current_segment = cache_entry->
m_buffer->current_segment_no();
754 if (current_segment && segment_no > current_segment)
756 uint32_t min_seek_segments = 0;
762 if (segment_no > current_segment + min_seek_segments)
770 if (repair_requested ||
794 if ((offset > cache_entry->
m_buffer->tell(segment_no)) &&
798 std::memset(buff, 0xFF, len);
822 if ((offset > cache_entry->
m_buffer->tell(segment_no)) &&
825 ((offset + len) > (cache_entry->
size())))
827 Logging::warning(cache_entry->
virtname(),
"Ignoring Windows' groundless access to the last 8K boundary of the file.");
855 if (cache_entry->
m_buffer->buffer_watermark(segment_no) < offset)
859 else if (cache_entry->
m_buffer->buffer_watermark(segment_no) < offset + len)
861 len = cache_entry->
m_buffer->buffer_watermark(segment_no) - offset;
866 if (!cache_entry->
m_buffer->copy(
reinterpret_cast<uint8_t*
>(buff), offset, len, segment_no))
882 catch (
bool _success)
887 *bytes_read =
static_cast<int>(len);
894 bool success =
false;
896 Logging::trace(cache_entry->
virtname(),
"Reading %1 bytes from offset %2 to %3 for frame no. %4.", len, offset, len + offset, frame_no);
920 std::vector<uint8_t> data;
926 success = cache_entry->
m_buffer->read_frame(&data, frame_no);
931 Logging::error(cache_entry->
virtname(),
"Reading image frame no. %1: (%2) %3", frame_no, errno, strerror(errno));
935 bool reported =
false;
962 Logging::error(cache_entry->
virtname(),
"Reading image frame no. %1: (%2) %3", frame_no, errno, strerror(errno));
993 Logging::error(cache_entry->
virtname(),
"Timeout reading image frame no. %1: (%2) %3", frame_no, errno, strerror(errno));
1011 Logging::trace(cache_entry->
virtname(),
"Frame no. %1: Cache miss at offset %<11zu>2 (length %<6u>3).", frame_no, offset, len);
1025 Logging::trace(cache_entry->
virtname(),
"Frame no. %1: Cache hit at offset %<11zu>2 (length %<6u>3).", frame_no, offset, len);
1032 if (data.size() < offset)
1036 else if (data.size() < offset + len)
1038 len = data.size() - offset;
1043 std::memcpy(buff, data.data() + offset, len);
1048 *bytes_read =
static_cast<int>(len);
1051 catch (
bool _success)
1061 cache->closeio(&cache_entry);
1066 return cache_entry->
size();
1071 return cache_entry->
m_buffer->buffer_watermark(segment_no);
1076 return cache_entry->
m_buffer->tell(segment_no);
1086 if (
cache !=
nullptr)
1088 return cache->maintenance();
1098 if (
cache !=
nullptr)
1100 return cache->clear();
1120 bool success =
true;
1122 const bool partial_multiformat_recode =
1126 if (partial_multiformat_recode)
1140 cache_entry->
clear();
1148 bool unlocked =
false;
1152 if (!cache_entry->
openio())
1154 throw (
static_cast<int>(errno));
1160 throw (
static_cast<int>(errno));
1185 throw (
static_cast<int>(errno));
1201 throw (
static_cast<int>(errno));
1209 throw (
static_cast<int>(errno));
1214 thread_data->m_initialised =
true;
1221 thread_data->m_thread_running_lock_guard =
true;
1222 thread_data->m_thread_running_cond.notify_all();
1258 throw (
static_cast<int>(errno));
1262 else if (transcoder.
is_hls())
1272 throw (
static_cast<int>(errno));
1282 throw (
static_cast<int>(errno));
1293 throw (
static_cast<int>(errno));
1302 thread_data->m_thread_running_lock_guard =
true;
1303 thread_data->m_thread_running_cond.notify_all();
1311 thread_data->m_thread_running_lock_guard =
true;
1312 thread_data->m_thread_running_cond.notify_all();
1334 thread_data->m_thread_running_lock_guard =
true;
1335 thread_data->m_thread_running_cond.notify_all();
1338 catch (
int _syserror)
1341 syserror = _syserror;
1343 if (!syserror && averror > -512)
1347 syserror = AVUNERROR(averror);
1357 thread_data->m_thread_running_lock_guard =
true;
1358 thread_data->m_thread_running_cond.notify_all();
1396 const std::chrono::steady_clock::time_point& start_time)
1398 if (cache_entry ==
nullptr)
1406 "Timeout! Transcoding aborted after %1 seconds inactivity.",
1419 const auto end_time = std::chrono::steady_clock::now();
1420 const auto elapsed_ms = std::chrono::duration<double, std::milli>(end_time - start_time).count();
1421 char elapsed_ms_text[32];
1423 std::snprintf(elapsed_ms_text,
sizeof(elapsed_ms_text),
"%.1f", elapsed_ms);
1426 "Transcoding completed successfully after %1 ms.",
1436 "System error: (%1) %2",
1444 "FFMpeg error: (%1) %2",
1457 Cache_Entry * cache_entry = thread_data->m_cache_entry;
1459 bool timeout =
false;
1460 bool success =
true;
1461 const auto start_time = std::chrono::steady_clock::now();
1463 std::unique_lock<std::recursive_mutex> lock_active_mutex(cache_entry->
m_active_mutex);
1464 std::unique_lock<std::recursive_mutex> lock_restart_mutex(cache_entry->
m_restart_mutex);
1466 uint32_t seek_frame = 0;
1472 Logging::error(transcoder.
virtname(),
"Transcoder completed with last seek frame to %1. Transcoder is being restarted.", seek_frame);
1475 success =
transcode(thread_data, cache_entry, transcoder, &timeout);
1522 if (
cache !=
nullptr)
1527 thread_data.reset();
#define CACHE_FLAG_RO
Mark cache file read-only.
#define CACHE_CLOSE_DELETE
Delete cache entry, will unlink cached file! Implies CACHE_CLOSE_FREE.
#define CACHE_CLOSE_NOOPT
Dummy, do nothing special.
@ FINISHED_INCOMPLETE
Transcode finished, but incomplete.
@ NONE
No result code available.
@ FINISHED_SUCCESS
Transcode finished successfully.
std::recursive_mutex m_restart_mutex
Mutex while thread is restarted.
size_t size() const
Return size of output file, as computed by encoder.
CACHE_INFO m_cache_info
Info about cached object.
bool update_access(bool update_database=false)
Update last access time.
std::atomic_bool m_suspend_timeout
true to temporarly disable read_frame timeout
bool is_finished() const
Get if cache has been finished.
bool suspend_timeout() const
Check for decode suspend timeout.
int ref_count() const
Get the current reference counter.
void lock()
Lock the access mutex.
std::atomic_bool m_is_decoding
true while file is decoding
const char * virtname() const
Return virtual filename. Same as destination filename, but with virtual (mount) path....
void clear(bool fetch_file_time=true)
Clear the cache entry.
LPVIRTUALFILE virtualfile()
Get the underlying VIRTUALFILE object.
bool openio(bool create_cache=true)
Open the cache file.
void update_read_count()
Update read counter.
const char * filename() const
Return source filename.
std::recursive_mutex m_active_mutex
Mutex while thread is active.
ID3v1 m_id3v1
ID3v1 structure which is used to send to clients.
unsigned int read_count() const
Get read counter.
bool flush()
Flush current memory cache to disk.
bool decode_timeout() const
Check for decode timeout.
void unlock()
Unlock the access mutex.
bool is_finished_incomplete() const
Get if cache has been finished, but not completely filled.
bool is_finished_success() const
Get if cache has been finished and filled successfully.
bool outdated() const
Check if cache entry needs to be recoded.
std::atomic_uint32_t m_seek_to_no
If not 0, seeks to specified frame.
std::unique_ptr< Buffer > m_buffer
Buffer object.
The FFmpeg_Transcoder class.
static bool video_size(size_t *filesize, AVCodecID codec_id, BITRATE bit_rate, int64_t duration, int width, int height, bool interleaved, const AVRational &framerate)
Predict video file size. This may (better will surely) be inaccurate.
int stack_seek_frame(uint32_t frame_no)
Seek to a specific frame. Does not actually perform the seek, this is done asynchronously by the tran...
virtual const char * filename() const override
Return source filename.
int process_single_fr(DECODER_STATUS *status)
static bool audio_size(size_t *filesize, AVCodecID codec_id, BITRATE bit_rate, int64_t duration, int channels, int sample_rate, AVSampleFormat sample_format)
Predict audio file size. This may (better will surely) be inaccurate.
bool have_seeked() const
Check if we made a seek operation.
void closeio()
Close transcoder, free all ressources.
uint32_t video_frame_count() const
Get the number of video frames in file.
bool is_hls() const
Check for HLS format.
size_t predicted_filesize() const
Try to predict the recoded file size. This may (better will surely) be inaccurate.
static bool total_overhead(size_t *filesize, FILETYPE filetype)
Predict overhead in file size. This may (better will surely) be inaccurate.
const ID3v1 * id3v1tag() const
Assemble an ID3v1 file tag.
uint32_t segment_count() const
Get the number of HLS segments of file.
bool is_multiformat() const
Check for an export frame format.
virtual const char * virtname() const override
Return virtual filename. Same as destination filename, but with virtual (mount) path....
uint32_t last_seek_frame_no() const
Current seek frame if available.
int open_output_file(Buffer *buffer)
Open output file. Data will actually be written to buffer and copied by FUSE when accessed.
int64_t duration() const
Get the file duration.
bool is_frameset() const
Check for an export frame format.
int64_t pts() const
Get PTS (presentation time stamp) of decoded audio/video so far.
int open_input_file(LPVIRTUALFILE virtualfile, std::shared_ptr< FileIO > fio=nullptr)
int stack_seek_segment(uint32_t segment_no)
Seek to a specific HLS segment. Does not actually perform the seek, this is done asynchronously by th...
static void warning(const T filename, const std::string &format_string, Args &&...args)
Write warning level log entry.
static void debug(const T filename, const std::string &format_string, Args &&...args)
Write debug level log entry.
static void trace(const T filename, const std::string &format_string, Args &&...args)
Write trace level log entry.
static void info(const T filename, const std::string &format_string, Args &&...args)
Write info level log entry.
static void error(const T filename, const std::string &format_string, Args &&...args)
Write error level log entry.
DECODER_STATUS
Decoder status codes. Can be error, success or end of file.
@ DEC_ERROR
Decoder error, see return code.
@ DEC_EOF
Read to end of file.
@ DEC_SUCCESS
Frame decoded successfully.
bool check_ignore(size_t size, size_t offset)
For use with win_smb_fix=1: Check if this an illegal access offset by Windows.
const char * get_codec_name(AVCodecID codec_id, bool long_name)
Safe way to get the codec name. Function never fails, will return "unknown" on error.
std::string get_filetype_text(FILETYPE filetype)
Convert FILETYPE enum to human readable text.
std::string format_time(time_t value)
Format a time in format "w d m s".
std::string format_result_size_ex(size_t size_resulting, size_t size_predicted)
Format size of transcoded file including difference between predicted and resulting size.
const std::string & append_sep(std::string *path)
Add / to the path if required.
const std::string & expand_path(std::string *tgt, const std::string &src)
Expand path, e.g., expand ~/ to home directory.
void mssleep(int milliseconds)
Sleep for specified time.
std::string format_size(uint64_t value)
Format size.
void stat_set_size(struct stat *st, size_t size)
Properly fill in all size related members in stat struct.
std::string format_size_ex(uint64_t value)
Format size.
std::string ffmpeg_geterror(int errnum)
Get FFmpeg error string for errnum. Internally calls av_strerror().
#define BITRATE
For FFmpeg bit rate is an int.
FFMPEGFS_PARAMS params
FFmpegfs command line parameters.
Main include for FFmpegfs project.
std::unique_ptr< thread_pool > tp
Thread pool object.
#define ID3V1_TAG_LENGTH
Fixed 128 bytes.
Provide various log facilities to stderr, disk or syslog.
uint32_t m_video_frame_count
Number of frames in video or 0 if not a video.
RESULTCODE m_result
Result code:
int64_t m_duration
File duration, in AV_TIME_BASE fractional seconds.
uint32_t m_segment_count
Number of segments for HLS.
size_t m_encoded_filesize
Actual file size after encode.
bool m_error
true if encode failed
size_t m_predicted_filesize
Predicted file size.
int m_averror
FFmpeg error code if encode failed.
int m_errno
errno if encode failed
time_t m_max_inactive_suspend
Time (seconds) that must elapse without access until transcoding is suspended.
const FFmpegfs_Format * current_format(LPCVIRTUALFILE virtualfile) const
Get FFmpegfs_Format for a virtual file.
int64_t m_min_seek_time_diff
Minimum time diff from current to next requested segment to perform a seek, in AV_TIME_BASE fractiona...
size_t m_prebuffer_size
Number of bytes that will be decoded before the output can be accessed.
time_t m_prebuffer_time
Playing time that will be decoded before the output can be accessed.
int64_t m_segment_duration
Duration of one HLS segment file, in AV_TIME_BASE fractional seconds.
std::string m_cachepath
Disk cache path, defaults to $XDG_CACHE_HOME.
int m_disable_cache
Disable cache.
time_t m_max_inactive_abort
Time (seconds) that must elapse without access until transcoding is aborted.
int m_win_smb_fix
Experimental Windows fix for access to EOF at file open.
THREAD_DATA struct to pass data from parent to child thread.
std::mutex m_thread_running_mutex
Mutex when thread is running.
bool m_initialised
True when this object is completely initialised.
std::atomic_bool m_thread_running_lock_guard
Lock guard to avoid spurious or missed unlocks.
Cache_Entry * m_cache_entry
Cache entry object. Will not be freed by child thread.
std::condition_variable m_thread_running_cond
Condition when thread is running.
size_t m_predicted_size
Use this as the size instead of computing it over and over.
struct stat m_st
stat structure with size etc.
uint32_t get_segment_count() const
Number of HLS segments in set.
uint32_t m_video_frame_count
Number of frames in video or 0 if not a video.
int64_t m_duration
Track/chapter duration, in AV_TIME_BASE fractional seconds.
Thread pool class implementation.
static bool transcode_until(Cache_Entry *cache_entry, size_t offset, size_t len, uint32_t segment_no)
Transcode the buffer until the buffer has enough or until an error occurs. The buffer needs at least ...
static bool transcode(std::shared_ptr< THREAD_DATA > thread_data, Cache_Entry *cache_entry, FFmpeg_Transcoder &transcoder, bool *timeout)
Actually transcode file.
const int TOTAL_RETRIES
Number of retries.
bool transcoder_predict_filesize(LPVIRTUALFILE virtualfile, Cache_Entry *cache_entry)
Predict file size.
static std::atomic_bool thread_exit
Used for shutdown: if true, forcibly exit all threads.
void transcoder_cache_path(std::string *path)
Get transcoder cache path.
Cache_Entry * transcoder_new(LPVIRTUALFILE virtualfile, bool begin_transcode)
Allocate and initialise the transcoder.
bool transcoder_set_filesize(LPVIRTUALFILE virtualfile, int64_t duration, BITRATE audio_bit_rate, int channels, int sample_rate, AVSampleFormat sample_format, BITRATE video_bit_rate, int width, int height, bool interleaved, const AVRational &framerate)
Set the file size.
static std::unique_ptr< Cache > cache
Global cache manager object.
static bool invalidate_stale_cache_file(Cache_Entry *cache_entry, uint32_t segment_no, uint32_t item_no, const char *item_name)
Invalidate a stale physical cache file and make the entry eligible for recoding.
static void log_transcoding_result(Cache_Entry *cache_entry, const FFmpeg_Transcoder &transcoder, bool timeout, bool success, const std::chrono::steady_clock::time_point &start_time)
Log the final result of a transcoder worker run.
void transcoder_free()
Free transcoder.
const int GRANULARITY
Image frame conversion: ms between checks if a picture frame is available.
size_t transcoder_buffer_tell(Cache_Entry *cache_entry, uint32_t segment_no)
Return the current file position in the file.
bool transcoder_read(Cache_Entry *cache_entry, char *buff, size_t offset, size_t len, int *bytes_read, uint32_t segment_no)
Read some bytes from the internal buffer and into the given buffer.
static int start_transcoder_thread(Cache_Entry *cache_entry)
Start the transcoder thread for an existing cache entry.
static int transcode_finish(Cache_Entry *cache_entry, FFmpeg_Transcoder &transcoder)
Close the input file and free everything but the initial buffer.
void transcoder_exit()
Exit transcoding.
size_t transcoder_get_size(Cache_Entry *cache_entry)
Return size of output file, as computed by encoder.
bool transcoder_read_frame(Cache_Entry *cache_entry, char *buff, size_t offset, size_t len, uint32_t frame_no, int *bytes_read, LPVIRTUALFILE virtualfile)
Read one image frame from the internal buffer and into the given buffer.
bool transcoder_init()
Initialise transcoder, create cache.
static bool cached_item_available(Cache_Entry *cache_entry, size_t offset, size_t len, uint32_t segment_no)
Check whether the requested cache data is available and physically readable.
size_t transcoder_buffer_watermark(Cache_Entry *cache_entry, uint32_t segment_no)
Return the current watermark of the file while transcoding.
bool transcoder_cache_clear()
Clear transcoder cache.
static int transcoder_thread(std::shared_ptr< THREAD_DATA > thread_data)
Transcoding thread.
bool transcoder_cached_filesize(LPVIRTUALFILE virtualfile, struct stat *stbuf)
Simply get encoded file size (do not create the whole encoder/decoder objects)
static void reopen_finished_incomplete_cache(Cache_Entry *cache_entry, uint32_t item_no, const char *item_name)
Re-open an incomplete multi-format cache for an on-demand repair run.
bool transcoder_cache_maintenance()
Run cache maintenance.
const int FRAME_TIMEOUT
Image frame conversion: timout seconds to wait if a picture frame is available.
void transcoder_delete(Cache_Entry *cache_entry)
Free the cache entry structure.
File transcoder interface (for use with by FUSE)