FFmpegfs Fuse Multi Media Filesystem 2.16
buffer.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017-2024 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() || m_cur_ci == nullptr)
352 {
353 return false;
354 }
355 return file_exists(m_ci[segment_no - 1].m_cachefile);
356}
357
358bool Buffer::map_file(const std::string & filename, volatile int *fd, uint8_t **p, size_t *filesize, bool *isdefaultsize, size_t defaultsize, bool truncate) const
359{
360 bool success = true;
361
362 if (!defaultsize)
363 {
364 Logging::trace(filename, "Mapping cache file.");
365 }
366 else
367 {
368 Logging::trace(filename, "Mapping cache file with %1.", format_size(defaultsize).c_str());
369 }
370
371 try
372 {
373 struct stat sb;
374
375 *fd = ::open(filename.c_str(), O_CREAT | O_RDWR | (truncate ? O_TRUNC : 0), static_cast<mode_t>(0644));
376 if (*fd == -1)
377 {
378 Logging::error(filename, "The cache file could not be opened due to an error: (%1) %2", errno, strerror(errno));
379 throw false;
380 }
381
382 if (fstat(*fd, &sb) == -1)
383 {
384 Logging::error(filename, "File stat failed: (%1) %2 (fd = %3)", errno, strerror(errno), *fd);
385 throw false;
386 }
387
388 if (!S_ISREG(sb.st_mode))
389 {
390 Logging::error(filename, "Not a file.");
391 throw false;
392 }
393
394 if (!sb.st_size || defaultsize)
395 {
396 // If file is empty or did not exist set file size to default
397
398 if (!defaultsize)
399 {
400 defaultsize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
401 }
402
403 if (ftruncate(*fd, static_cast<off_t>(defaultsize)) == -1)
404 {
405 Logging::error(filename, "Error calling ftruncate() to 'stretch' the file: (%1) %2 (fd = %3)", errno, strerror(errno), *fd);
406 throw false;
407 }
408
409 *filesize = defaultsize;
410 *isdefaultsize = true;
411 }
412 else
413 {
414 // Keep size
415 *filesize = static_cast<size_t>(sb.st_size);
416 *isdefaultsize = false;
417 }
418
419 *p = static_cast<uint8_t *>(mmap(nullptr, *filesize, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0));
420 if (*p == MAP_FAILED)
421 {
422 Logging::error(filename, "File mapping failed: (%1) %2 (fd = %3)", errno, strerror(errno), *fd);
423 *p = nullptr;
424 throw false;
425 }
426 }
427 catch (bool _success)
428 {
429 success = _success;
430
431 if (!success && *fd != -1)
432 {
433 ::close(*fd);
434 *fd = -1;
435 }
436 }
437
438 return success;
439}
440
441bool Buffer::unmap_file(const std::string &filename, volatile int *fd, uint8_t **p, size_t len, size_t * filesize) const
442{
443 bool success = true;
444
445 Logging::trace(filename, "Unmapping cache file.");
446
447 void * _p = *p;
448 size_t _filesize = *filesize;
449 int _fd = *fd;
450
451 // Clear all variables
452 *p = nullptr;
453 *filesize = 0;
454 *fd = -1;
455
456 if (_p != nullptr)
457 {
458 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)
459 {
460 Logging::error(filename, "Unmapping cache file failed: (%1) %2 Size: %3", errno, strerror(errno), len);
461 success = false;
462 }
463 }
464
465 if (_fd != -1)
466 {
467 if (_filesize)
468 {
469 if (ftruncate(_fd, static_cast<off_t>(_filesize)) == -1)
470 {
471 Logging::error(filename, "Error calling ftruncate() to resize and close the cache file: (%1) %2 (fd = %3) Size: %5", errno, strerror(errno), _fd, _filesize);
472 success = false;
473 }
474 ::close(_fd);
475 }
476 else
477 {
478 ::close(_fd);
479
480 if (unlink(filename.c_str()) && errno != ENOENT) // Ignore if file does not exist
481 {
482 Logging::error(filename, "Error removing the cache file: (%1) %2 (fd = %3)", errno, strerror(errno), _fd);
483 success = false;
484 }
485 }
486 }
487
488 return success;
489}
490
491bool Buffer::release(int flags /*= CACHE_CLOSE_NOOPT*/)
492{
493 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
494
495 bool success = true;
496
497 if (!is_open())
498 {
500 {
501 for (uint32_t n = 1; n <= segment_count(); n++)
502 {
504 }
505 errno = 0; // ignore this error
506 }
507
508 return true;
509 }
510
511 // Write active cache to disk
512 flush();
513
514 // Close anything that's still open
515 for (uint32_t index = 0; index < segment_count(); index++)
516 {
517 if (!close_file(index + 1, CACHE_FLAG_RO | CACHE_FLAG_RW))
518 {
519 success = false;
520 }
521
523 {
524 remove_cachefile(index + 1);
525 errno = 0; // ignore this error
526 }
527 }
528
529 // Remove index for frame sets. There is only one.
530 if (!m_ci[0].m_cachefile_idx.empty())
531 {
532 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))
533 {
534 success = false;
535 }
536 }
537
538 return success;
539}
540
541bool Buffer::remove_cachefile(uint32_t segment_no) const
542{
543 const CACHEINFO & ci = !segment_no ? *m_cur_ci : m_ci[segment_no - 1];
544 bool success = remove_file(ci.m_cachefile);
545
546 if (!ci.m_cachefile_idx.empty())
547 {
549 {
550 success = false;
551 }
552 }
553 return success;
554}
555
557{
558 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
559
560 if (!segment_count() || m_cur_ci == nullptr || m_cur_ci->m_buffer == nullptr)
561 {
562 errno = EPERM;
563 return false;
564 }
565
566 if (msync(m_cur_ci->m_buffer, m_cur_ci->m_buffer_size, MS_SYNC) == -1)
567 {
568 Logging::error(m_cur_ci->m_cachefile, "Could not sync to disk: (%1) %2", errno, strerror(errno));
569 return false;
570 }
571
572 if (m_cur_ci->m_buffer_idx != nullptr)
573 {
574 if (msync(m_cur_ci->m_buffer_idx, m_cur_ci->m_buffer_size_idx, MS_SYNC) == -1)
575 {
576 Logging::error(m_cur_ci->m_cachefile_idx, "Could not sync to disk: (%1) %2", errno, strerror(errno));
577 return false;
578 }
579 }
580
581 return true;
582}
583
585{
586 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
587
588 bool success = true;
589
590 for (CACHEINFO & ci : m_ci)
591 {
592 ci.m_buffer_pos = 0;
593 ci.m_buffer_watermark = 0;
594 ci.m_buffer_size = 0;
595 ci.m_seg_finished = false;
596 ci.m_buffer_write_size = 0;
597 ci.m_buffer_writes = 0;
598
599 if (ci.m_fd != -1)
600 {
601 // If empty set file size to 1 page
602 long filesize = sysconf(_SC_PAGESIZE);
603
604 if (ftruncate(ci.m_fd, filesize) == -1)
605 {
606 Logging::error(ci.m_cachefile, "Error calling ftruncate() to clear the file: (%1) %2 (fd = %3)", errno, strerror(errno), ci.m_fd);
607 success = false;
608 }
609 }
610 else
611 {
612 remove_file(ci.m_cachefile);
613 }
614
615 if (ci.m_fd_idx != -1)
616 {
617 std::memset(ci.m_buffer_idx, 0, ci.m_buffer_size_idx);
618 }
619 }
620
621 return success;
622}
623
624bool Buffer::reserve(size_t size)
625{
626 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
627
628 if (m_cur_ci == nullptr)
629 {
630 errno = ENOMEM;
631 Logging::error(nullptr, "INTERNAL ERROR: Buffer::reserve() - m_cur_ci == nullptr!");
632 return false;
633 }
634
635 if (m_cur_ci->m_buffer == nullptr)
636 {
637 errno = ENOMEM;
638 Logging::error(nullptr, "INTERNAL ERROR: Buffer::reserve() - m_cur_ci->m_buffer == nullptr!");
639 return false;
640 }
641
643 {
644 // Do not shrink
645 return true;
646 }
647
648 m_cur_ci->m_buffer = static_cast<uint8_t*>(mremap(m_cur_ci->m_buffer, m_cur_ci->m_buffer_size, size, MREMAP_MAYMOVE));
649 if (m_cur_ci->m_buffer == MAP_FAILED)
650 {
651 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);
652 m_cur_ci->m_buffer = nullptr;
653 return false;
654 }
655
656 // Save size
658
659 if (ftruncate(m_cur_ci->m_fd, static_cast<off_t>(m_cur_ci->m_buffer_size)) == -1)
660 {
661 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);
662 return false;
663 }
664
665 return true;
666}
667
668size_t Buffer::writeio(const uint8_t* data, size_t length)
669{
670 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
671
672 if (m_cur_ci == nullptr || m_cur_ci->m_buffer == nullptr)
673 {
674 errno = ENOMEM;
675 return 0;
676 }
677
678 uint8_t* write_ptr = write_prepare(length);
679 if (write_ptr == nullptr)
680 {
681 length = 0;
682 }
683 else
684 {
685 m_cur_ci->m_buffer_write_size += length;
687
688 std::memcpy(write_ptr, data, length);
689 increment_pos(length);
690 }
691
692 return length;
693}
694
695size_t Buffer::write_frame(const uint8_t *data, size_t length, uint32_t frame_no)
696{
697 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
698
699 if (data == nullptr || m_cur_ci == nullptr || m_cur_ci->m_buffer_idx == nullptr || frame_no < 1 || frame_no > virtualfile()->m_video_frame_count)
700 {
701 // Invalid parameter
702 errno = EINVAL;
703 return 0;
704 }
705
706 LPIMAGE_FRAME old_image_frame;
707 IMAGE_FRAME new_image_frame;
708 size_t bytes_written;
709 size_t start = static_cast<size_t>(frame_no - 1) * sizeof(IMAGE_FRAME);
710
711 old_image_frame = reinterpret_cast<LPIMAGE_FRAME>(m_cur_ci->m_buffer_idx + start);
712
713 if (old_image_frame->m_frame_no && (old_image_frame->m_size <= static_cast<uint32_t>(length)))
714 {
715 // Frame already exists and has enough space
716 old_image_frame->m_size = static_cast<uint32_t>(length);
717
718 // Write image
719 seek(static_cast<long>(old_image_frame->m_offset), SEEK_SET);
720 bytes_written = writeio(data, old_image_frame->m_size);
721 if (bytes_written != old_image_frame->m_size)
722 {
723 return 0;
724 }
725 }
726 else
727 {
728 // Create new frame if not existing or not enough space
729 std::memset(&new_image_frame, 0xFF, sizeof(new_image_frame));
730 std::memcpy(new_image_frame.m_tag.data(), IMAGE_FRAME_TAG, sizeof(new_image_frame.m_tag));
731 new_image_frame.m_frame_no = frame_no;
732 new_image_frame.m_offset = buffer_watermark();
733 new_image_frame.m_size = static_cast<uint32_t>(length);
734
735 // Write image
736 seek(static_cast<long>(new_image_frame.m_offset), SEEK_SET);
737 bytes_written = writeio(data, new_image_frame.m_size);
738 if (bytes_written != new_image_frame.m_size)
739 {
740 return 0;
741 }
742
743 std::memcpy(reinterpret_cast<void *>(m_cur_ci->m_buffer_idx + start), &new_image_frame, sizeof(IMAGE_FRAME));
744 }
745
746 return bytes_written;
747}
748
749uint8_t* Buffer::write_prepare(size_t length)
750{
751 if (reallocate(m_cur_ci->m_buffer_pos + length))
752 {
754 {
756 }
758 }
759 else
760 {
761 errno = ESPIPE;
762 return nullptr;
763 }
764}
765
766void Buffer::increment_pos(size_t increment)
767{
768 m_cur_ci->m_buffer_pos += increment;
769}
770
771int Buffer::seek(int64_t offset, int whence)
772{
773 return seek(offset, whence, 0);
774}
775
776int Buffer::seek(int64_t offset, int whence, uint32_t segment_no)
777{
778 LPCACHEINFO ci = cacheinfo(segment_no);
779
780 if (ci == nullptr || ci->m_buffer == nullptr)
781 {
782 errno = ENOMEM;
783 return (EOF);
784 }
785
786 off_t seek_pos;
787
788 switch (whence)
789 {
790 case SEEK_SET:
791 {
792 seek_pos = offset;
793 break;
794 }
795 case SEEK_CUR:
796 {
797 seek_pos = static_cast<off_t>(tell(segment_no)) + offset;
798 break;
799 }
800 case SEEK_END:
801 {
802 seek_pos = static_cast<off_t>(size(segment_no)) + offset;
803 break;
804 }
805 default:
806 {
807 errno = EINVAL;
808 return (EOF);
809 }
810 }
811
812 if (seek_pos > static_cast<off_t>(size(segment_no)))
813 {
814 ci->m_buffer_pos = size(segment_no); // Cannot go beyond EOF. Set position to end, leave errno untouched.
815 return 0;
816 }
817
818 if (seek_pos < 0) // Cannot go before head, leave position untouched, set errno.
819 {
820 errno = EINVAL;
821 return (EOF);
822 }
823
824 ci->m_buffer_pos = static_cast<size_t>(seek_pos);
825 return 0;
826}
827
828size_t Buffer::tell() const
829{
830 return tell(0);
831}
832
833size_t Buffer::tell(uint32_t segment_no) const
834{
835 LPCCACHEINFO ci = const_cacheinfo(segment_no);
836
837 if (ci == nullptr)
838 {
839 errno = EBADF;
840 return 0;
841 }
842
843 return ci->m_buffer_pos;
844}
845
846int64_t Buffer::duration() const
847{
848 return AV_NOPTS_VALUE; // not applicable
849}
850
851size_t Buffer::size() const
852{
853 return size(0);
854}
855
856size_t Buffer::size(uint32_t segment_no) const
857{
858 LPCCACHEINFO ci = const_cacheinfo(segment_no);
859
860 if (ci == nullptr)
861 {
862 errno = EBADF;
863 return 0;
864 }
865
866 return ci->m_buffer_size;
867}
868
869size_t Buffer::buffer_watermark(uint32_t segment_no) const
870{
871 LPCCACHEINFO ci = const_cacheinfo(segment_no);
872
873 if (ci == nullptr)
874 {
875 errno = EBADF;
876 return 0;
877 }
878
879 return ci->m_buffer_watermark;
880}
881
882bool Buffer::copy(std::vector<uint8_t> * out_data, size_t offset, uint32_t segment_no)
883{
884 return copy(out_data->data(), offset, out_data->size(), segment_no);
885}
886
887bool Buffer::copy(uint8_t* out_data, size_t offset, size_t bufsize, uint32_t segment_no)
888{
889 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
890
891 LPCCACHEINFO ci = const_cacheinfo(segment_no);
892
893 if (ci == nullptr)
894 {
895 errno = EBADF;
896 return false;
897 }
898
899 if (ci->m_buffer == nullptr)
900 {
901 errno = ENOMEM;
902 return false;
903 }
904
905 size_t segment_size = ci->m_buffer_size;
906
907 if (!segment_size && errno)
908 {
909 Logging::error(ci->m_cachefile, "INTERNAL ERROR: Buffer::copy()! size(segment_no) returned error. Segment: %1 (%2) %3", segment_no, errno, strerror(errno));
910 return false;
911 }
912
913 if (segment_size > offset)
914 {
915 if (segment_size < offset + bufsize)
916 {
917 bufsize = segment_size - offset - 1;
918 }
919
920 std::memcpy(out_data, ci->m_buffer + offset, bufsize);
921
922 return true;
923 }
924 else
925 {
926 Logging::error(ci->m_cachefile, "INTERNAL ERROR: Buffer::copy()! segment_size <= offset - Segment: %1 Segment Size: %2 Offset: %3", segment_no, segment_size, offset);
927 errno = ESPIPE;
928
929 return false;
930 }
931}
932
933bool Buffer::reallocate(size_t newsize)
934{
935 if (newsize > size())
936 {
938 {
939 size_t alloc_size = newsize - size();
941 size_t write_size = PREALLOC_FACTOR * write_avg;
942 if (write_size > alloc_size)
943 {
944 alloc_size = write_size;
945
946 newsize = size() + alloc_size;
947
948 }
949 }
950
951 Logging::trace(filename(), "Buffer reallocate: %1 -> %2 (Diff %3).", size(), newsize, newsize - size());
952
953 if (!reserve(newsize))
954 {
955 return false;
956 }
957 }
958 return true;
959}
960
961const std::string & Buffer::cachefile(uint32_t segment_no) const
962{
963 LPCCACHEINFO ci = const_cacheinfo(segment_no);
964
965 if (ci == nullptr)
966 {
967 static std::string empty;
968 errno = EBADF;
969 return empty;
970 }
971
972 return ci->m_cachefile;
973}
974
975const std::string & Buffer::make_cachefile_name(std::string * cachefile, const std::string & filename, const std::string & fileext, bool is_idx)
976{
978
981
982 if (is_idx)
983 {
984 *cachefile += ".idx.";
985 }
986 else
987 {
988 *cachefile += ".cache.";
989 }
990 *cachefile += fileext;
991
992 return *cachefile;
993}
994
995bool Buffer::remove_file(const std::string & filename)
996{
997 if (unlink(filename.c_str()) && errno != ENOENT)
998 {
999 Logging::warning(filename, "Cannot unlink the file: (%1) %2", errno, strerror(errno));
1000 return false;
1001 }
1002 else
1003 {
1004 errno = 0;
1005 return true;
1006 }
1007}
1008
1009size_t Buffer::readio(void * /*data*/, size_t /*size*/)
1010{
1011 // Not implemented
1012 errno = EPERM;
1013 return 0;
1014}
1015
1016size_t Buffer::read_frame(std::vector<uint8_t> * data, uint32_t frame_no)
1017{
1018 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
1019
1020 if (data == nullptr || m_cur_ci->m_buffer_idx == nullptr || frame_no < 1 || frame_no > virtualfile()->m_video_frame_count)
1021 {
1022 // Invalid parameter
1023 errno = EINVAL;
1024 return 0;
1025 }
1026
1027 LPCIMAGE_FRAME image_frame;
1028 size_t start = static_cast<size_t>(frame_no - 1) * sizeof(IMAGE_FRAME);
1029
1030 image_frame = reinterpret_cast<LPCIMAGE_FRAME>(m_cur_ci->m_buffer_idx + start);
1031
1032 if (!image_frame->m_frame_no)
1033 {
1034 errno = EAGAIN;
1035 return 0;
1036 }
1037
1038 data->resize(image_frame->m_size);
1039
1040 return copy(data, static_cast<size_t>(image_frame->m_offset));
1041}
1042
1043int Buffer::error() const
1044{
1045 return errno;
1046}
1047
1048bool Buffer::eof() const
1049{
1050 return eof(0);
1051}
1052
1053bool Buffer::eof(uint32_t segment_no) const
1054{
1055 return (tell(segment_no) == size(segment_no));
1056}
1057
1059{
1060 release();
1061}
1062
1063bool Buffer::have_frame(uint32_t frame_no)
1064{
1065 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
1066
1067 if (m_cur_ci->m_buffer_idx == nullptr || frame_no < 1 || frame_no > virtualfile()->m_video_frame_count)
1068 {
1069 // Invalid parameter
1070 errno = EINVAL;
1071 return false;
1072 }
1073
1074 LPCIMAGE_FRAME image_frame;
1075 size_t start = static_cast<size_t>(frame_no - 1) * sizeof(IMAGE_FRAME);
1076
1077 image_frame = reinterpret_cast<LPCIMAGE_FRAME>(m_cur_ci->m_buffer_idx + start);
1078
1079 return (image_frame->m_frame_no ? true : false);
1080}
1081
1083{
1084 std::lock_guard<std::recursive_mutex> lock_mutex(m_mutex);
1085
1086 for (const CACHEINFO & ci : m_ci)
1087 {
1088 if ((ci.m_fd != -1 && (fcntl(ci.m_fd, F_GETFL) != -1 || errno != EBADF)))
1089 {
1090 return true;
1091 }
1092 }
1093
1094 return false;
1095}
1096
1098{
1099 if (m_cur_ci == nullptr)
1100 {
1101 return;
1102 }
1103
1104 m_cur_ci->m_seg_finished = true;
1105
1106 flush();
1107}
1108
1109bool Buffer::is_segment_finished(uint32_t segment_no) const
1110{
1111 LPCCACHEINFO ci = const_cacheinfo(segment_no);
1112
1113 if (ci == nullptr)
1114 {
1115 errno = EBADF;
1116 return false;
1117 }
1118
1119 return ci->m_seg_finished;
1120}
1121
1123{
1124 if (segment_no)
1125 {
1126 segment_no--;
1127
1128 if (segment_no >= segment_count())
1129 {
1130 return nullptr;
1131 }
1132 return (&m_ci[segment_no]);
1133 }
1134
1135 return m_cur_ci;
1136}
1137
1139{
1140 if (segment_no)
1141 {
1142 segment_no--;
1143
1144 if (segment_no >= m_ci.size())
1145 {
1146 return nullptr;
1147 }
1148 return (&m_ci[segment_no]);
1149 }
1150
1151 return m_cur_ci;
1152}
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
std::vector< CACHEINFO > m_ci
Cache info.
Definition: buffer.h:477
virtual int64_t duration() const override
Get the duration of the file, in AV_TIME_BASE fractional seconds.
Definition: buffer.cc:846
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:1048
void increment_pos(size_t increment)
Increment buffer position.
Definition: buffer.cc:766
LPCACHEINFO cacheinfo(uint32_t segment_no)
Get cache information.
Definition: buffer.cc:1122
uint8_t * write_prepare(size_t length)
Prepare for the writing operation.
Definition: buffer.cc:749
bool release(int flags=CACHE_CLOSE_NOOPT)
Release cache buffer.
Definition: buffer.cc:491
uint32_t m_cur_open
Number of open files.
Definition: buffer.h:475
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:1097
virtual int seek(int64_t offset, int whence) override
Seek to position in file.
Definition: buffer.cc:771
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:668
bool clear()
Clear (delete) buffer.
Definition: buffer.cc:584
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:358
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:441
virtual void closeio() override
Close buffer.
Definition: buffer.cc:1058
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:473
bool is_segment_finished(uint32_t segment_no) const
Return true if transcoding of the segment is finished.
Definition: buffer.cc:1109
static bool remove_file(const std::string &filename)
Remove (unlink) the file.
Definition: buffer.cc:995
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:695
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:961
virtual size_t readio(void *data, size_t size) override
Not implemented.
Definition: buffer.cc:1009
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:1063
bool flush()
Flush buffer to disk.
Definition: buffer.cc:556
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:975
bool reserve(size_t size)
Reserve memory without changing size to reduce re-allocations.
Definition: buffer.cc:624
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:933
virtual int error() const override
Get last error.
Definition: buffer.cc:1043
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:882
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:1138
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:828
LPCACHEINFO m_cur_ci
Convenience pointer to current write segment.
Definition: buffer.h:474
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:541
Buffer()
Create Buffer object.
Definition: buffer.cc:42
bool is_open()
Check if the cache file is open.
Definition: buffer.cc:1082
virtual size_t size() const override
Get the value of the internal buffer size pointer.
Definition: buffer.cc:851
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:869
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:1016
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:74
Main include for FFmpegfs project.
void transcoder_cache_path(std::string *path)
Get transcoder cache path.
Definition: transcode.cc:195
VIRTUALTYPE
Virtual file types enum.
Definition: fileio.h:92
@ BUFFER
Buffer file.
struct IMAGE_FRAME IMAGE_FRAME
Image frame header.
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
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
volatile int m_fd
File handle for buffer.
Definition: buffer.h:104
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:235
std::string m_mountpath
Mount path: Files from m_mountpath will be mapped to this directory.
Definition: ffmpegfs.h:197
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