FFmpegfs Fuse Multi Media Filesystem 2.19
Loading...
Searching...
No Matches
buffer.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017-2026 Norbert Schlia (nschlia@oblivion-software.de)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * On Debian systems, the complete text of the GNU General Public License
19 * Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
20 */
21
32#include "buffer.h"
33#include "ffmpegfs.h"
34#include "logging.h"
35
36#include <unistd.h>
37#include <sys/mman.h>
38#include <libgen.h>
39#include <cstring>
40
41// Initially Buffer is empty. It will be allocated as needed.
43 : m_cur_ci(nullptr)
44 , m_cur_open(0)
45{
46}
47
48// If buffer_data was never allocated, this is a no-op.
50{
51 release();
52}
53
55{
57}
58
59size_t Buffer::bufsize() const
60{
61 return 0; // Not applicable
62}
63
65{
66 if (virtualfile == nullptr)
67 {
68 errno = EINVAL;
69 return (EOF);
70 }
71
73
74 return 0;
75}
76
77bool Buffer::open_file(uint32_t segment_no, uint32_t flags, size_t defaultsize)
78{
79 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
80
81 uint32_t index = segment_no;
82 if (index)
83 {
84 index--;
85 }
86
87 CACHEINFO & ci = m_ci[index];
88
89 ci.m_flags |= flags;
90
91 if (ci.m_fd != -1)
92 {
93 Logging::trace(ci.m_cachefile, "Cache file is already open.");
94
95 if (defaultsize)
96 {
97 // Make sure the requested size is available
98 reserve(defaultsize);
99 }
100 // Already open
101 return true;
102 }
103
104 if (flags & CACHE_FLAG_RW)
105 {
106 Logging::debug(ci.m_cachefile, "Writing to cache file.");
107 }
108 else
109 {
110 Logging::debug(ci.m_cachefile, "Reading from cache file.");
111 }
112
113 size_t filesize = 0;
114 bool isdefaultsize = false;
115 uint8_t *p = nullptr;
116
117 if (!map_file(ci.m_cachefile, &ci.m_fd, &p, &filesize, &isdefaultsize, defaultsize, (flags & CACHE_FLAG_RW) ? true : false))
118 {
119 return false;
120 }
121
122 if (!isdefaultsize)
123 {
124 ci.m_buffer_pos = ci.m_buffer_watermark = filesize;
125 }
126
127 ci.m_buffer_size = filesize;
128 ci.m_buffer = static_cast<uint8_t*>(p);
129 ci.m_buffer_write_size = 0;
130 ci.m_buffer_writes = 0;
131
132 ++m_cur_open; // track open files
133
134 return true;
135}
136
137bool Buffer::close_file(uint32_t segment_no, uint32_t flags)
138{
139 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
140
141 uint32_t index = segment_no;
142 if (index)
143 {
144 index--;
145 }
146
147 CACHEINFO & ci = m_ci[index];
148
149 ci.m_flags &= ~flags;
150
151 if (ci.m_flags)
152 {
153 Logging::trace(ci.m_cachefile, "While attempting to close, the cache file is still in use. Currently open: %1", m_cur_open);
154 return true;
155 }
156
157 if (ci.m_fd == -1)
158 {
159 // Already closed
160 Logging::trace(ci.m_cachefile, "No need to close the unopened cache file. Currently open: %1", m_cur_open);
161 return true;
162 }
163
164 Logging::trace(ci.m_cachefile, "Closing cache file.");
165
166 bool success = unmap_file(ci.m_cachefile, &ci.m_fd, &ci.m_buffer, ci.m_buffer_size, &ci.m_buffer_watermark);
167
168 ci.m_buffer_pos = 0;
169 ci.m_buffer_size = 0;
170
171 if (success && m_cur_open > 0)
172 {
173 --m_cur_open; // track open files
174 }
175
176 return success;
177}
178
179bool Buffer::init(bool erase_cache)
180{
181 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
182
183 if (is_open())
184 {
185 return true;
186 }
187
188 bool success = true;
189
190 try
191 {
192 if ((virtualfile()->m_flags & VIRTUALFLAG_HLS))
193 {
194 // HLS format: create several segments
195 if (virtualfile()->get_segment_count())
196 {
197 m_ci.resize(virtualfile()->get_segment_count());
198
199 for (uint32_t segment_no = 1; segment_no <= virtualfile()->get_segment_count(); segment_no++)
200 {
201 make_cachefile_name(&m_ci[segment_no - 1].m_cachefile, filename() + "." + make_filename(segment_no, params.current_format(virtualfile())->fileext()), params.current_format(virtualfile())->fileext(), false);
202 }
203 }
204 else
205 {
206 Logging::error(filename(), "INTERNAL ERROR: Buffer::init()! Segment count is 0.");
207 errno = EINVAL;
208 throw false;
209 }
210 }
211 else
212 {
213 // All other formats: create just a single segment.
214 m_ci.resize(1);
215
216 make_cachefile_name(&m_ci[0].m_cachefile, filename(), params.current_format(virtualfile())->fileext(), false);
217 if ((virtualfile()->m_flags & VIRTUALFLAG_FRAME))
218 {
219 // Create extra index cash for frame sets only
220 make_cachefile_name(&m_ci[0].m_cachefile_idx, filename(), params.current_format(virtualfile())->fileext(), true);
221 }
222 }
223
224 // Set current segment
225 m_cur_ci = &m_ci[0];
226
227 // Create the path to the cache file. All paths are the same, so this is required only once.
228 std::shared_ptr<char[]> cachefiletmp = new_strdup(m_ci[0].m_cachefile);
229
230 if (cachefiletmp == nullptr)
231 {
232 Logging::error(m_ci[0].m_cachefile, "Error opening the cache file: out of memory.");
233 errno = ENOMEM;
234 throw false;
235 }
236
237 if (mktree(dirname(cachefiletmp.get()), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) && errno != EEXIST)
238 {
239 Logging::error(m_ci[0].m_cachefile, "Error creating cache directory: (%1) %2", errno, strerror(errno));
240 throw false;
241 }
242 errno = 0; // reset EEXIST, error can safely be ignored here
243
244#if __cplusplus >= 202002L
245 // C++20 (and later) code
246 for (uint32_t index = 0; CACHEINFO & ci : m_ci)
247#else
248 uint32_t index = 0;
249 for (CACHEINFO & ci : m_ci)
250#endif
251 {
252 ci.reset();
253
254 if (erase_cache)
255 {
256 remove_cachefile(index + 1);
257 errno = 0; // ignore this error
258 }
259
260 index++;
261 }
262
263 // Index only required for frame sets and there is only one.
264 if (!m_ci[0].m_cachefile_idx.empty())
265 {
266 if (virtualfile()->m_video_frame_count == 0)
267 {
268 errno = EINVAL;
269 Logging::error(m_ci[0].m_cachefile, "INTERNAL ERROR: Buffer::init()! Frame count is zero (%1) %2", errno, strerror(errno));
270 throw false;
271 }
272 static_assert(sizeof(IMAGE_FRAME) == 32, "sizeof(IMAGE_FRAME) must be 32 bytes");
273
274 size_t filesize = 0;
275 bool isdefaultsize = false;
276 uint8_t *p = nullptr;
277
278 if (!map_file(m_ci[0].m_cachefile_idx, &m_ci[0].m_fd_idx, &p, &filesize, &isdefaultsize, sizeof(IMAGE_FRAME) * virtualfile()->m_video_frame_count, false))
279 {
280 throw false;
281 }
282
283 m_ci[0].m_buffer_size_idx = filesize;
284 m_ci[0].m_buffer_idx = static_cast<uint8_t*>(p);
285 }
286 }
287 catch (bool _success)
288 {
289 success = _success;
290
291 if (!success)
292 {
293 for (CACHEINFO & ci : m_ci)
294 {
295 ci.reset();
296 }
297 }
298 }
299
300 return success;
301}
302
303bool Buffer::set_segment(uint32_t segment_no, size_t size)
304{
305 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
306
307 if (!segment_no || segment_no > segment_count())
308 {
309 errno = EINVAL;
310 return false;
311 }
312
314 {
315 return false;
316 }
317
318 if (!open_file(segment_no, CACHE_FLAG_RW, size))
319 {
320 return false;
321 }
322
323 m_cur_ci = &m_ci[segment_no - 1];
324
325 // Reserve enough buffer space for segment to avoid frequent resizes
326 return reserve(size);
327}
328
330{
331 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
332
333 return static_cast<uint32_t>(m_ci.size());
334}
335
337{
338 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
339
340 if (!segment_count() || m_cur_ci == nullptr)
341 {
342 return 0;
343 }
344 return static_cast<uint32_t>(m_cur_ci - &m_ci[0]) + 1;
345}
346
347bool Buffer::segment_exists(uint32_t segment_no)
348{
349 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
350
351 if (!segment_count())
352 {
353 return false;
354 }
355
356 uint32_t index = segment_no;
357 if (index)
358 {
359 index--;
360 }
361
362 if (index >= m_ci.size())
363 {
364 errno = EBADF;
365 return false;
366 }
367
368 return file_exists(m_ci[index].m_cachefile);
369}
370
371bool Buffer::cachefile_valid(uint32_t segment_no)
372{
373 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
374
375 LPCCACHEINFO ci = const_cacheinfo(segment_no);
376
377 if (ci == nullptr)
378 {
379 errno = EBADF;
380 return false;
381 }
382
383 struct stat sb;
384 if (stat(ci->m_cachefile.c_str(), &sb) == -1)
385 {
386 return false;
387 }
388
389 if (!S_ISREG(sb.st_mode))
390 {
391 errno = EINVAL;
392 return false;
393 }
394
395 if (sb.st_size <= 0)
396 {
397 errno = ENODATA;
398 return false;
399 }
400
401 errno = 0;
402 return true;
403}
404
405bool Buffer::invalidate_segment(uint32_t segment_no)
406{
407 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
408
409 LPCACHEINFO ci = cacheinfo(segment_no);
410
411 if (ci == nullptr)
412 {
413 errno = EBADF;
414 return false;
415 }
416
417 bool success = true;
418
419 ci->m_seg_finished = false;
420 ci->m_buffer_pos = 0;
421 ci->m_buffer_watermark = 0;
422 ci->m_buffer_write_size = 0;
423 ci->m_buffer_writes = 0;
424 ci->m_flags = 0;
425
426 if (ci->m_fd != -1 || ci->m_buffer != nullptr)
427 {
428 if (!unmap_file(ci->m_cachefile, &ci->m_fd, &ci->m_buffer, ci->m_buffer_size, &ci->m_buffer_watermark))
429 {
430 success = false;
431 }
432
433 if (m_cur_open > 0)
434 {
435 --m_cur_open;
436 }
437 }
438
439 ci->m_buffer_pos = 0;
440 ci->m_buffer_watermark = 0;
441 ci->m_buffer_size = 0;
442
443 if (!remove_file(ci->m_cachefile))
444 {
445 success = false;
446 }
447
448 if (segment_no == 0 && ci->m_buffer_idx != nullptr && ci->m_buffer_size_idx)
449 {
450 std::memset(ci->m_buffer_idx, 0, ci->m_buffer_size_idx);
451 }
452
453 return success;
454}
455
456bool Buffer::map_file(const std::string & filename, volatile int *fd, uint8_t **p, size_t *filesize, bool *isdefaultsize, size_t defaultsize, bool truncate) const
457{
458 bool success = true;
459
460 if (!defaultsize)
461 {
462 Logging::trace(filename, "Mapping cache file.");
463 }
464 else
465 {
466 Logging::trace(filename, "Mapping cache file with %1.", format_size(defaultsize).c_str());
467 }
468
469 try
470 {
471 struct stat sb;
472
473 *fd = ::open(filename.c_str(), O_CREAT | O_RDWR | (truncate ? O_TRUNC : 0), static_cast<mode_t>(0644));
474 if (*fd == -1)
475 {
476 Logging::error(filename, "The cache file could not be opened due to an error: (%1) %2", errno, strerror(errno));
477 throw false;
478 }
479
480 if (fstat(*fd, &sb) == -1)
481 {
482 Logging::error(filename, "File stat failed: (%1) %2 (fd = %3)", errno, strerror(errno), *fd);
483 throw false;
484 }
485
486 if (!S_ISREG(sb.st_mode))
487 {
488 Logging::error(filename, "Not a file.");
489 throw false;
490 }
491
492 if (!sb.st_size || defaultsize)
493 {
494 // If file is empty or did not exist set file size to default
495
496 if (!defaultsize)
497 {
498 defaultsize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
499 }
500
501 if (ftruncate(*fd, static_cast<off_t>(defaultsize)) == -1)
502 {
503 Logging::error(filename, "Error calling ftruncate() to 'stretch' the file: (%1) %2 (fd = %3)", errno, strerror(errno), *fd);
504 throw false;
505 }
506
507 *filesize = defaultsize;
508 *isdefaultsize = true;
509 }
510 else
511 {
512 // Keep size
513 *filesize = static_cast<size_t>(sb.st_size);
514 *isdefaultsize = false;
515 }
516
517 *p = static_cast<uint8_t *>(mmap(nullptr, *filesize, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0));
518 if (*p == MAP_FAILED)
519 {
520 Logging::error(filename, "File mapping failed: (%1) %2 (fd = %3)", errno, strerror(errno), *fd);
521 *p = nullptr;
522 throw false;
523 }
524 }
525 catch (bool _success)
526 {
527 success = _success;
528
529 if (!success && *fd != -1)
530 {
531 ::close(*fd);
532 *fd = -1;
533 }
534 }
535
536 return success;
537}
538
539bool Buffer::unmap_file(const std::string &filename, volatile int *fd, uint8_t **p, size_t len, size_t * filesize) const
540{
541 bool success = true;
542
543 Logging::trace(filename, "Unmapping cache file.");
544
545 void * _p = *p;
546 size_t _filesize = *filesize;
547 int _fd = *fd;
548
549 // Clear all variables
550 *p = nullptr;
551 *filesize = 0;
552 *fd = -1;
553
554 if (_p != nullptr)
555 {
556 if (munmap(_p, len ? len : static_cast<size_t>(sysconf(_SC_PAGESIZE))) == -1) // Make sure we do not unmap a zero size file (spits EINVAL error)
557 {
558 Logging::error(filename, "Unmapping cache file failed: (%1) %2 Size: %3", errno, strerror(errno), len);
559 success = false;
560 }
561 }
562
563 if (_fd != -1)
564 {
565 if (_filesize)
566 {
567 if (ftruncate(_fd, static_cast<off_t>(_filesize)) == -1)
568 {
569 Logging::error(filename, "Error calling ftruncate() to resize and close the cache file: (%1) %2 (fd = %3) Size: %5", errno, strerror(errno), _fd, _filesize);
570 success = false;
571 }
572 ::close(_fd);
573 }
574 else
575 {
576 ::close(_fd);
577
578 if (unlink(filename.c_str()) && errno != ENOENT) // Ignore if file does not exist
579 {
580 Logging::error(filename, "Error removing the cache file: (%1) %2 (fd = %3)", errno, strerror(errno), _fd);
581 success = false;
582 }
583 }
584 }
585
586 return success;
587}
588
589bool Buffer::release(int flags /*= CACHE_CLOSE_NOOPT*/)
590{
591 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
592
593 bool success = true;
594
595 if (!is_open())
596 {
598 {
599 for (uint32_t n = 1; n <= segment_count(); n++)
600 {
602 }
603 errno = 0; // ignore this error
604 }
605
606 return true;
607 }
608
609 // Write active cache to disk
610 flush();
611
612 // Close anything that's still open
613 for (uint32_t index = 0; index < segment_count(); index++)
614 {
615 if (!close_file(index + 1, CACHE_FLAG_RO | CACHE_FLAG_RW))
616 {
617 success = false;
618 }
619
621 {
622 remove_cachefile(index + 1);
623 errno = 0; // ignore this error
624 }
625 }
626
627 // Remove index for frame sets. There is only one.
628 if (!m_ci[0].m_cachefile_idx.empty())
629 {
630 if (!unmap_file(m_ci[0].m_cachefile_idx, &m_ci[0].m_fd_idx, &m_ci[0].m_buffer_idx, m_ci[0].m_buffer_size_idx, &m_ci[0].m_buffer_size_idx))
631 {
632 success = false;
633 }
634 }
635
636 return success;
637}
638
639bool Buffer::remove_cachefile(uint32_t segment_no) const
640{
641 const CACHEINFO & ci = !segment_no ? *m_cur_ci : m_ci[segment_no - 1];
642 bool success = remove_file(ci.m_cachefile);
643
644 if (!ci.m_cachefile_idx.empty())
645 {
647 {
648 success = false;
649 }
650 }
651 return success;
652}
653
655{
656 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
657
658 if (!segment_count() || m_cur_ci == nullptr || m_cur_ci->m_buffer == nullptr)
659 {
660 errno = EPERM;
661 return false;
662 }
663
664 if (msync(m_cur_ci->m_buffer, m_cur_ci->m_buffer_size, MS_SYNC) == -1)
665 {
666 Logging::error(m_cur_ci->m_cachefile, "Could not sync to disk: (%1) %2", errno, strerror(errno));
667 return false;
668 }
669
670 if (m_cur_ci->m_buffer_idx != nullptr)
671 {
672 if (msync(m_cur_ci->m_buffer_idx, m_cur_ci->m_buffer_size_idx, MS_SYNC) == -1)
673 {
674 Logging::error(m_cur_ci->m_cachefile_idx, "Could not sync to disk: (%1) %2", errno, strerror(errno));
675 return false;
676 }
677 }
678
679 return true;
680}
681
683{
684 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
685
686 bool success = true;
687
688 for (CACHEINFO & ci : m_ci)
689 {
690 ci.m_buffer_pos = 0;
691 ci.m_buffer_watermark = 0;
692 ci.m_buffer_size = 0;
693 ci.m_seg_finished = false;
694 ci.m_buffer_write_size = 0;
695 ci.m_buffer_writes = 0;
696
697 if (ci.m_fd != -1)
698 {
699 // If empty set file size to 1 page
700 long filesize = sysconf(_SC_PAGESIZE);
701
702 if (ftruncate(ci.m_fd, filesize) == -1)
703 {
704 Logging::error(ci.m_cachefile, "Error calling ftruncate() to clear the file: (%1) %2 (fd = %3)", errno, strerror(errno), ci.m_fd);
705 success = false;
706 }
707 }
708 else
709 {
710 remove_file(ci.m_cachefile);
711 }
712
713 if (ci.m_fd_idx != -1)
714 {
715 std::memset(ci.m_buffer_idx, 0, ci.m_buffer_size_idx);
716 }
717 }
718
719 return success;
720}
721
722bool Buffer::reserve(size_t size)
723{
724 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
725
726 if (m_cur_ci == nullptr)
727 {
728 errno = ENOMEM;
729 Logging::error(nullptr, "INTERNAL ERROR: Buffer::reserve() - m_cur_ci == nullptr!");
730 return false;
731 }
732
733 if (m_cur_ci->m_buffer == nullptr)
734 {
735 errno = ENOMEM;
736 Logging::error(nullptr, "INTERNAL ERROR: Buffer::reserve() - m_cur_ci->m_buffer == nullptr!");
737 return false;
738 }
739
741 {
742 // Do not shrink
743 return true;
744 }
745
746 m_cur_ci->m_buffer = static_cast<uint8_t*>(mremap(m_cur_ci->m_buffer, m_cur_ci->m_buffer_size, size, MREMAP_MAYMOVE));
747 if (m_cur_ci->m_buffer == MAP_FAILED)
748 {
749 Logging::error(m_cur_ci->m_cachefile, "Error calling mremap() to resize the file: (%1) %2 (fd = %3) Old size: %4 New: %5", errno, strerror(errno), m_cur_ci->m_fd, m_cur_ci->m_buffer_size, size);
750 m_cur_ci->m_buffer = nullptr;
751 return false;
752 }
753
754 // Save size
756
757 if (ftruncate(m_cur_ci->m_fd, static_cast<off_t>(m_cur_ci->m_buffer_size)) == -1)
758 {
759 Logging::error(m_cur_ci->m_cachefile, "Error calling ftruncate() to resize the file: (%1) %2 (fd = %3)", errno, strerror(errno), m_cur_ci->m_fd);
760 return false;
761 }
762
763 return true;
764}
765
766size_t Buffer::writeio(const uint8_t* data, size_t length)
767{
768 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
769
770 if (m_cur_ci == nullptr || m_cur_ci->m_buffer == nullptr)
771 {
772 errno = ENOMEM;
773 return 0;
774 }
775
776 uint8_t* write_ptr = write_prepare(length);
777 if (write_ptr == nullptr)
778 {
779 length = 0;
780 }
781 else
782 {
783 m_cur_ci->m_buffer_write_size += length;
785
786 std::memcpy(write_ptr, data, length);
787 increment_pos(length);
788 }
789
790 return length;
791}
792
793size_t Buffer::write_frame(const uint8_t *data, size_t length, uint32_t frame_no)
794{
795 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
796
797 if (data == nullptr || m_cur_ci == nullptr || m_cur_ci->m_buffer_idx == nullptr || frame_no < 1 || frame_no > virtualfile()->m_video_frame_count)
798 {
799 // Invalid parameter
800 errno = EINVAL;
801 return 0;
802 }
803
804 LPIMAGE_FRAME old_image_frame;
805 IMAGE_FRAME new_image_frame;
806 size_t bytes_written;
807 size_t start = static_cast<size_t>(frame_no - 1) * sizeof(IMAGE_FRAME);
808
809 old_image_frame = reinterpret_cast<LPIMAGE_FRAME>(m_cur_ci->m_buffer_idx + start);
810
811 if (old_image_frame->m_frame_no && (old_image_frame->m_size <= static_cast<uint32_t>(length)))
812 {
813 // Frame already exists and has enough space
814 old_image_frame->m_size = static_cast<uint32_t>(length);
815
816 // Write image
817 seek(static_cast<long>(old_image_frame->m_offset), SEEK_SET);
818 bytes_written = writeio(data, old_image_frame->m_size);
819 if (bytes_written != old_image_frame->m_size)
820 {
821 return 0;
822 }
823 }
824 else
825 {
826 // Create new frame if not existing or not enough space
827 std::memset(&new_image_frame, 0xFF, sizeof(new_image_frame));
828 std::memcpy(new_image_frame.m_tag.data(), IMAGE_FRAME_TAG, sizeof(new_image_frame.m_tag));
829 new_image_frame.m_frame_no = frame_no;
830 new_image_frame.m_offset = buffer_watermark();
831 new_image_frame.m_size = static_cast<uint32_t>(length);
832
833 // Write image
834 seek(static_cast<long>(new_image_frame.m_offset), SEEK_SET);
835 bytes_written = writeio(data, new_image_frame.m_size);
836 if (bytes_written != new_image_frame.m_size)
837 {
838 return 0;
839 }
840
841 std::memcpy(reinterpret_cast<void *>(m_cur_ci->m_buffer_idx + start), &new_image_frame, sizeof(IMAGE_FRAME));
842 }
843
844 return bytes_written;
845}
846
847uint8_t* Buffer::write_prepare(size_t length)
848{
849 if (reallocate(m_cur_ci->m_buffer_pos + length))
850 {
852 {
854 }
856 }
857 else
858 {
859 errno = ESPIPE;
860 return nullptr;
861 }
862}
863
864void Buffer::increment_pos(size_t increment)
865{
866 m_cur_ci->m_buffer_pos += increment;
867}
868
869int Buffer::seek(int64_t offset, int whence)
870{
871 return seek(offset, whence, 0);
872}
873
874int Buffer::seek(int64_t offset, int whence, uint32_t segment_no)
875{
876 LPCACHEINFO ci = cacheinfo(segment_no);
877
878 if (ci == nullptr || ci->m_buffer == nullptr)
879 {
880 errno = ENOMEM;
881 return (EOF);
882 }
883
884 off_t seek_pos;
885
886 switch (whence)
887 {
888 case SEEK_SET:
889 {
890 seek_pos = offset;
891 break;
892 }
893 case SEEK_CUR:
894 {
895 seek_pos = static_cast<off_t>(tell(segment_no)) + offset;
896 break;
897 }
898 case SEEK_END:
899 {
900 seek_pos = static_cast<off_t>(size(segment_no)) + offset;
901 break;
902 }
903 default:
904 {
905 errno = EINVAL;
906 return (EOF);
907 }
908 }
909
910 if (seek_pos > static_cast<off_t>(size(segment_no)))
911 {
912 ci->m_buffer_pos = size(segment_no); // Cannot go beyond EOF. Set position to end, leave errno untouched.
913 return 0;
914 }
915
916 if (seek_pos < 0) // Cannot go before head, leave position untouched, set errno.
917 {
918 errno = EINVAL;
919 return (EOF);
920 }
921
922 ci->m_buffer_pos = static_cast<size_t>(seek_pos);
923 return 0;
924}
925
926size_t Buffer::tell() const
927{
928 return tell(0);
929}
930
931size_t Buffer::tell(uint32_t segment_no) const
932{
933 LPCCACHEINFO ci = const_cacheinfo(segment_no);
934
935 if (ci == nullptr)
936 {
937 errno = EBADF;
938 return 0;
939 }
940
941 return ci->m_buffer_pos;
942}
943
944int64_t Buffer::duration() const
945{
946 return AV_NOPTS_VALUE; // not applicable
947}
948
949size_t Buffer::size() const
950{
951 return size(0);
952}
953
954size_t Buffer::size(uint32_t segment_no) const
955{
956 LPCCACHEINFO ci = const_cacheinfo(segment_no);
957
958 if (ci == nullptr)
959 {
960 errno = EBADF;
961 return 0;
962 }
963
964 return ci->m_buffer_size;
965}
966
967size_t Buffer::buffer_watermark(uint32_t segment_no) const
968{
969 LPCCACHEINFO ci = const_cacheinfo(segment_no);
970
971 if (ci == nullptr)
972 {
973 errno = EBADF;
974 return 0;
975 }
976
977 return ci->m_buffer_watermark;
978}
979
980bool Buffer::copy(std::vector<uint8_t> * out_data, size_t offset, uint32_t segment_no)
981{
982 return copy(out_data->data(), offset, out_data->size(), segment_no);
983}
984
985bool Buffer::copy(uint8_t* out_data, size_t offset, size_t bufsize, uint32_t segment_no)
986{
987 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
988
989 LPCCACHEINFO ci = const_cacheinfo(segment_no);
990
991 if (ci == nullptr)
992 {
993 errno = EBADF;
994 return false;
995 }
996
997 if (ci->m_buffer == nullptr)
998 {
999 errno = ENOMEM;
1000 return false;
1001 }
1002
1003 size_t segment_size = ci->m_buffer_size;
1004
1005 if (!segment_size && errno)
1006 {
1007 Logging::error(ci->m_cachefile, "INTERNAL ERROR: Buffer::copy()! size(segment_no) returned error. Segment: %1 (%2) %3", segment_no, errno, strerror(errno));
1008 return false;
1009 }
1010
1011 if (segment_size > offset)
1012 {
1013 if (segment_size < offset + bufsize)
1014 {
1015 bufsize = segment_size - offset - 1;
1016 }
1017
1018 std::memcpy(out_data, ci->m_buffer + offset, bufsize);
1019
1020 return true;
1021 }
1022 else
1023 {
1024 Logging::error(ci->m_cachefile, "INTERNAL ERROR: Buffer::copy()! segment_size <= offset - Segment: %1 Segment Size: %2 Offset: %3", segment_no, segment_size, offset);
1025 errno = ESPIPE;
1026
1027 return false;
1028 }
1029}
1030
1031bool Buffer::reallocate(size_t newsize)
1032{
1033 if (newsize > size())
1034 {
1036 {
1037 size_t alloc_size = newsize - size();
1039 size_t write_size = PREALLOC_FACTOR * write_avg;
1040 if (write_size > alloc_size)
1041 {
1042 alloc_size = write_size;
1043
1044 newsize = size() + alloc_size;
1045
1046 }
1047 }
1048
1049 Logging::trace(filename(), "Buffer reallocate: %1 -> %2 (Diff %3).", size(), newsize, newsize - size());
1050
1051 if (!reserve(newsize))
1052 {
1053 return false;
1054 }
1055 }
1056 return true;
1057}
1058
1059const std::string & Buffer::cachefile(uint32_t segment_no) const
1060{
1061 LPCCACHEINFO ci = const_cacheinfo(segment_no);
1062
1063 if (ci == nullptr)
1064 {
1065 static std::string empty;
1066 errno = EBADF;
1067 return empty;
1068 }
1069
1070 return ci->m_cachefile;
1071}
1072
1073const std::string & Buffer::make_cachefile_name(std::string * cachefile, const std::string & filename, const std::string & fileext, bool is_idx)
1074{
1076
1078 *cachefile += filename;
1079
1080 if (is_idx)
1081 {
1082 *cachefile += ".idx.";
1083 }
1084 else
1085 {
1086 *cachefile += ".cache.";
1087 }
1088 *cachefile += fileext;
1089
1090 return *cachefile;
1091}
1092
1093bool Buffer::remove_file(const std::string & filename)
1094{
1095 if (unlink(filename.c_str()) && errno != ENOENT)
1096 {
1097 Logging::warning(filename, "Cannot unlink the file: (%1) %2", errno, strerror(errno));
1098 return false;
1099 }
1100 else
1101 {
1102 errno = 0;
1103 return true;
1104 }
1105}
1106
1107size_t Buffer::readio(void * /*data*/, size_t /*size*/)
1108{
1109 // Not implemented
1110 errno = EPERM;
1111 return 0;
1112}
1113
1114size_t Buffer::read_frame(std::vector<uint8_t> * data, uint32_t frame_no)
1115{
1116 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
1117
1118 if (data == nullptr || m_cur_ci->m_buffer_idx == nullptr || frame_no < 1 || frame_no > virtualfile()->m_video_frame_count)
1119 {
1120 // Invalid parameter
1121 errno = EINVAL;
1122 return 0;
1123 }
1124
1125 LPCIMAGE_FRAME image_frame;
1126 size_t start = static_cast<size_t>(frame_no - 1) * sizeof(IMAGE_FRAME);
1127
1128 image_frame = reinterpret_cast<LPCIMAGE_FRAME>(m_cur_ci->m_buffer_idx + start);
1129
1130 if (!image_frame->m_frame_no)
1131 {
1132 errno = EAGAIN;
1133 return 0;
1134 }
1135
1136 data->resize(image_frame->m_size);
1137
1138 return copy(data, static_cast<size_t>(image_frame->m_offset));
1139}
1140
1141int Buffer::error() const
1142{
1143 return errno;
1144}
1145
1146bool Buffer::eof() const
1147{
1148 return eof(0);
1149}
1150
1151bool Buffer::eof(uint32_t segment_no) const
1152{
1153 return (tell(segment_no) == size(segment_no));
1154}
1155
1157{
1158 release();
1159}
1160
1161bool Buffer::have_frame(uint32_t frame_no)
1162{
1163 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
1164
1165 if (m_cur_ci->m_buffer_idx == nullptr || frame_no < 1 || frame_no > virtualfile()->m_video_frame_count)
1166 {
1167 // Invalid parameter
1168 errno = EINVAL;
1169 return false;
1170 }
1171
1172 LPCIMAGE_FRAME image_frame;
1173 size_t start = static_cast<size_t>(frame_no - 1) * sizeof(IMAGE_FRAME);
1174
1175 image_frame = reinterpret_cast<LPCIMAGE_FRAME>(m_cur_ci->m_buffer_idx + start);
1176
1177 return (image_frame->m_frame_no ? true : false);
1178}
1179
1181{
1182 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
1183
1184 for (const CACHEINFO & ci : m_ci)
1185 {
1186 if ((ci.m_fd != -1 && (fcntl(ci.m_fd, F_GETFL) != -1 || errno != EBADF)))
1187 {
1188 return true;
1189 }
1190 }
1191
1192 return false;
1193}
1194
1196{
1197 if (m_cur_ci == nullptr)
1198 {
1199 return;
1200 }
1201
1202 m_cur_ci->m_seg_finished = true;
1203
1204 flush();
1205}
1206
1207bool Buffer::is_segment_finished(uint32_t segment_no) const
1208{
1209 LPCCACHEINFO ci = const_cacheinfo(segment_no);
1210
1211 if (ci == nullptr)
1212 {
1213 errno = EBADF;
1214 return false;
1215 }
1216
1217 return ci->m_seg_finished;
1218}
1219
1221{
1222 if (segment_no)
1223 {
1224 segment_no--;
1225
1226 if (segment_no >= segment_count())
1227 {
1228 return nullptr;
1229 }
1230 return (&m_ci[segment_no]);
1231 }
1232
1233 return m_cur_ci;
1234}
1235
1237{
1238 if (segment_no)
1239 {
1240 segment_no--;
1241
1242 if (segment_no >= m_ci.size())
1243 {
1244 return nullptr;
1245 }
1246 return (&m_ci[segment_no]);
1247 }
1248
1249 return m_cur_ci;
1250}
Buffer class.
#define CACHE_FLAG_RO
Mark cache file read-only.
Definition buffer.h:49
#define CACHE_FLAG_RW
Mark cache file writeable, implies read permissions.
Definition buffer.h:50
#define CACHE_CHECK_BIT(mask, var)
Check bit in bitmask.
Definition buffer.h:43
#define CACHE_CLOSE_DELETE
Delete cache entry, will unlink cached file! Implies CACHE_CLOSE_FREE.
Definition buffer.h:47
bool invalidate_segment(uint32_t segment_no=0)
Invalidate the requested cache segment/file.
Definition buffer.cc:405
std::vector< CACHEINFO > m_ci
Cache info.
Definition buffer.h:501
virtual int64_t duration() const override
Get the duration of the file, in AV_TIME_BASE fractional seconds.
Definition buffer.cc:944
static constexpr int PREALLOC_FACTOR
PREALLOC_FACTOR - Number of elements allocated on reallocate calls Number of elements allocated on re...
Definition buffer.h:64
virtual bool eof() const override
Check if at end of file.
Definition buffer.cc:1146
void increment_pos(size_t increment)
Increment buffer position.
Definition buffer.cc:864
LPCACHEINFO cacheinfo(uint32_t segment_no)
Get cache information.
Definition buffer.cc:1220
uint8_t * write_prepare(size_t length)
Prepare for the writing operation.
Definition buffer.cc:847
bool release(int flags=CACHE_CLOSE_NOOPT)
Release cache buffer.
Definition buffer.cc:589
uint32_t m_cur_open
Number of open files.
Definition buffer.h:499
virtual int openio(LPVIRTUALFILE virtualfile) override
Open a virtual file.
Definition buffer.cc:64
bool segment_exists(uint32_t segment_no)
Check if segment exists.
Definition buffer.cc:347
void finished_segment()
Complete the segment decoding.
Definition buffer.cc:1195
bool cachefile_valid(uint32_t segment_no)
Check whether the physical cache file for the requested segment exists and contains data.
Definition buffer.cc:371
virtual int seek(int64_t offset, int whence) override
Seek to position in file.
Definition buffer.cc:869
size_t writeio(const uint8_t *data, size_t length)
Write data to the current position in the buffer. The position pointer will be updated.
Definition buffer.cc:766
bool clear()
Clear (delete) buffer.
Definition buffer.cc:682
bool map_file(const std::string &filename, volatile int *fd, uint8_t **p, size_t *filesize, bool *isdefaultsize, size_t defaultsize, bool truncate) const
Map memory to a file.
Definition buffer.cc:456
virtual ~Buffer()
Free Buffer object.
Definition buffer.cc:49
bool unmap_file(const std::string &filename, volatile int *fd, uint8_t **p, size_t len, size_t *filesize) const
Unmap memory from the file.
Definition buffer.cc:539
virtual void closeio() override
Close buffer.
Definition buffer.cc:1156
virtual VIRTUALTYPE type() const override
Get type of this virtual file.
Definition buffer.cc:54
bool init(bool erase_cache)
Initialise cache.
Definition buffer.cc:179
std::recursive_mutex m_mutex
Access mutex.
Definition buffer.h:497
bool is_segment_finished(uint32_t segment_no) const
Return true if transcoding of the segment is finished.
Definition buffer.cc:1207
static bool remove_file(const std::string &filename)
Remove (unlink) the file.
Definition buffer.cc:1093
size_t write_frame(const uint8_t *data, size_t length, uint32_t frame_no)
Write image data for the frame number into the buffer.
Definition buffer.cc:793
CACHEINFO const * LPCCACHEINFO
Pointer to const version of CACHEINFO.
Definition buffer.h:121
const std::string & cachefile(uint32_t segment_no) const
Get cache filename.
Definition buffer.cc:1059
virtual size_t readio(void *data, size_t size) override
Not implemented.
Definition buffer.cc:1107
bool have_frame(uint32_t frame_no)
Check if we have the requested frame number. Works only when processing a frame set.
Definition buffer.cc:1161
bool flush()
Flush buffer to disk.
Definition buffer.cc:654
static const std::string & make_cachefile_name(std::string *cachefile, const std::string &filename, const std::string &fileext, bool is_idx)
Make up a cache file name, including the full path.
Definition buffer.cc:1073
bool reserve(size_t size)
Reserve memory without changing size to reduce re-allocations.
Definition buffer.cc:722
uint32_t current_segment_no()
Get the currently selected segment.
Definition buffer.cc:336
bool reallocate(size_t newsize)
Reallocate the buffer to a new size.
Definition buffer.cc:1031
virtual int error() const override
Get last error.
Definition buffer.cc:1141
bool copy(std::vector< uint8_t > *out_data, size_t offset, uint32_t segment_no=0)
Copy buffered data into output buffer.
Definition buffer.cc:980
virtual size_t bufsize() const override
Size of this buffer.
Definition buffer.cc:59
LPCCACHEINFO const_cacheinfo(uint32_t segment_no) const
Get cache information.
Definition buffer.cc:1236
uint32_t segment_count()
Get segment count.
Definition buffer.cc:329
virtual size_t tell() const override
Get the value of the internal read position pointer.
Definition buffer.cc:926
LPCACHEINFO m_cur_ci
Convenience pointer to current write segment.
Definition buffer.h:498
bool set_segment(uint32_t segment_no, size_t size)
Set the current segment.
Definition buffer.cc:303
bool remove_cachefile(uint32_t segment_no=0) const
Remove the cachefile.
Definition buffer.cc:639
Buffer()
Create Buffer object.
Definition buffer.cc:42
bool is_open()
Check if the cache file is open.
Definition buffer.cc:1180
virtual size_t size() const override
Get the value of the internal buffer size pointer.
Definition buffer.cc:949
bool open_file(uint32_t segment_no, uint32_t flags, size_t defaultsize=0)
Open the cache file if not already open.
Definition buffer.cc:77
bool close_file(uint32_t segment_no, uint32_t flags)
If it hasn't already been done, close the cache file.
Definition buffer.cc:137
size_t buffer_watermark(uint32_t segment_no=0) const
Return the current watermark of the file while transcoding.
Definition buffer.cc:967
size_t read_frame(std::vector< uint8_t > *data, uint32_t frame_no)
Write image data for the frame number into the buffer.
Definition buffer.cc:1114
LPVIRTUALFILE virtualfile()
Get virtual file object.
Definition fileio.cc:113
void set_virtualfile(LPVIRTUALFILE virtualfile)
Set the virtual file object.
Definition fileio.cc:96
const std::string & filename() const
Get source filename.
Definition fileio.cc:123
static void warning(const T filename, const std::string &format_string, Args &&...args)
Write warning level log entry.
Definition logging.h:220
static void debug(const T filename, const std::string &format_string, Args &&...args)
Write debug level log entry.
Definition logging.h:182
static void trace(const T filename, const std::string &format_string, Args &&...args)
Write trace level log entry.
Definition logging.h:163
static void error(const T filename, const std::string &format_string, Args &&...args)
Write error level log entry.
Definition logging.h:239
int mktree(const std::string &path, mode_t mode)
Make directory tree.
std::shared_ptr< char[]> new_strdup(const std::string &str)
strdup() variant taking a std::string as input.
std::string make_filename(uint32_t file_no, const std::string &fileext)
Make a file name from file number and file extension.
bool file_exists(const std::string &filename)
Check if file exists.
std::string format_size(uint64_t value)
Format size.
FFMPEGFS_PARAMS params
FFmpegfs command line parameters.
Definition ffmpegfs.cc:81
Main include for FFmpegfs project.
void transcoder_cache_path(std::string *path)
Get transcoder cache path.
Definition transcode.cc:336
VIRTUALTYPE
Virtual file types enum.
Definition fileio.h:92
@ BUFFER
Buffer file.
IMAGE_FRAME const * LPCIMAGE_FRAME
Pointer version of IMAGE_FRAME.
Definition fileio.h:86
#define VIRTUALFLAG_HLS
File is part of a set of HLS transport stream (ts) files.
Definition fileio.h:116
#define VIRTUALFLAG_FRAME
File is part of a set of frames.
Definition fileio.h:115
#define IMAGE_FRAME_TAG
Tag of an image frame header for the frame images buffer.
Definition fileio.h:68
Provide various log facilities to stderr, disk or syslog.
Structure to hold current cache state.
Definition buffer.h:70
int m_fd
File handle for buffer.
Definition buffer.h:104
size_t m_buffer_write_size
Sum of bytes written to the buffer.
Definition buffer.h:118
uint8_t * m_buffer
Pointer to buffer memory.
Definition buffer.h:105
bool m_seg_finished
True if segment completely decoded.
Definition buffer.h:109
unsigned int m_buffer_writes
Total number of writes to the buffer.
Definition buffer.h:119
size_t m_buffer_size_idx
Size of index buffer.
Definition buffer.h:114
uint8_t * m_buffer_idx
Pointer to index memory.
Definition buffer.h:113
size_t m_buffer_size
Current buffer size.
Definition buffer.h:108
std::string m_cachefile
Cache file name.
Definition buffer.h:103
uint32_t m_flags
CACHE_FLAG_* options.
Definition buffer.h:116
size_t m_buffer_watermark
Number of bytes in buffer.
Definition buffer.h:107
size_t m_buffer_pos
Read/write position.
Definition buffer.h:106
std::string m_cachefile_idx
Index file name.
Definition buffer.h:111
const FFmpegfs_Format * current_format(LPCVIRTUALFILE virtualfile) const
Get FFmpegfs_Format for a virtual file.
Definition ffmpegfs.cc:242
std::string m_mountpath
Mount path: Files from m_mountpath will be mapped to this directory.
Definition ffmpegfs.h:193
Image frame header.
Definition fileio.h:77
uint64_t m_offset
Offset in index file.
Definition fileio.h:80
uint32_t m_size
Image size in bytes.
Definition fileio.h:81
uint32_t m_frame_no
Number of the frame image. 0 if not yet decoded.
Definition fileio.h:79
std::array< char, 8 > m_tag
Start tag, always ascii "IMGFRAME".
Definition fileio.h:78
Virtual file definition.
Definition fileio.h:123
uint32_t get_segment_count() const
Number of HLS segments in set.
Definition fileio.cc:45
uint32_t m_video_frame_count
Number of frames in video or 0 if not a video.
Definition fileio.h:158