diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2022-10-05 16:09:31 +0200 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2022-10-05 16:09:31 +0200 | 
| commit | 7c1c779cfd01162d020d0c276bb605adfe338df9 (patch) | |
| tree | 415265244ecc90de6e01ff705b25edc67b813448 | |
| parent | b76df807e61e25e07fa3ee075dab255355b278fd (diff) | |
| download | dabmux-7c1c779cfd01162d020d0c276bb605adfe338df9.tar.gz dabmux-7c1c779cfd01162d020d0c276bb605adfe338df9.tar.bz2 dabmux-7c1c779cfd01162d020d0c276bb605adfe338df9.zip | |
Add load_entire_file option for file inputs
| -rw-r--r-- | doc/advanced.mux | 15 | ||||
| -rw-r--r-- | src/ConfigParser.cpp | 14 | ||||
| -rw-r--r-- | src/input/File.cpp | 142 | ||||
| -rw-r--r-- | src/input/File.h | 15 | 
4 files changed, 159 insertions, 27 deletions
| diff --git a/doc/advanced.mux b/doc/advanced.mux index fd7a06c..61328d7 100644 --- a/doc/advanced.mux +++ b/doc/advanced.mux @@ -278,6 +278,20 @@ subchannels {         ; (1 << 20) + (1 << 17) + (1 << 0) = 0x120001         ;inputuri "prbs://:0x120001     } + +   ; An example using 'enhancedpacket' to send out epg data +   ; See http://wiki.opendigitalradio.org/How_to_configure_SPI_(Service_and_Programme_Information)_for_ODR-DabMux +    sub-data { +        id 6 +        type enhancedpacket +        ; Settings this flag to true makes ODR-DabMux preload the entire file contents into memory +        ; This allows you to replace the file contents while the current file data still gets transmitted to the +        ; end. +        load_entire_file true +        inputfile "./epg.dat" +        protection 1 +        bitrate 32 +    }  }  ; For now, each component links one service to one subchannel @@ -299,6 +313,7 @@ components {          ; FIG 0/13 user application was previously configured using the figtype setting, which          ; allowed only a single user application. +        ; Now more than one user application can be defined per component, using the          ; Using the 'user-applications' section, several can be defined per component.          ; Do not use both figtype and user-applications. diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index dbbc2fb..a845bef 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -1090,16 +1090,24 @@ static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan,          throw runtime_error(ss.str());      } -    const bool nonblock = pt.get("nonblock", false); -    if (nonblock) { +    if (pt.get("nonblock", false)) {          if (auto filein = dynamic_pointer_cast<Inputs::FileBase>(subchan->input)) { -            filein->setNonblocking(nonblock); +            filein->setNonblocking(true);          }          else {              etiLog.level(warn) << "The nonblock option is not supported";          }      } +    if (pt.get("load_entire_file", false)) { +        if (auto filein = dynamic_pointer_cast<Inputs::FileBase>(subchan->input)) { +            filein->setLoadEntireFile(true); +        } +        else { +            etiLog.level(warn) << "The load_entire_file option is not supported"; +        } +    } +      const string bufferManagement = pt.get("buffer-management", "prebuffering");      if (bufferManagement == "prebuffering") {          subchan->input->setBufferManagement(Inputs::BufferManagement::Prebuffering); diff --git a/src/input/File.cpp b/src/input/File.cpp index 46bfb59..8f5d3e3 100644 --- a/src/input/File.cpp +++ b/src/input/File.cpp @@ -2,7 +2,7 @@     Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2019 Matthias P. Braendli +   Copyright (C) 2022 Matthias P. Braendli      http://www.opendigitalradio.org     */ @@ -62,15 +62,22 @@ __attribute((packed))  void FileBase::open(const std::string& name)  { -    int flags = O_RDONLY | O_BINARY; -    if (m_nonblock) { -        flags |= O_NONBLOCK; +    m_filename = name; + +    if (m_load_entire_file) { +        load_entire_file();      } +    else { +        int flags = O_RDONLY | O_BINARY; +        if (m_nonblock) { +            flags |= O_NONBLOCK; +        } -    m_fd = ::open(name.c_str(), flags); -    if (m_fd == -1) { -        throw runtime_error("Could not open input file " + name + ": " + -            strerror(errno)); +        m_fd = ::open(name.c_str(), flags); +        if (m_fd == -1) { +            throw runtime_error("Could not open input file " + name + ": " + +                    strerror(errno)); +        }      }  } @@ -102,15 +109,82 @@ void FileBase::close()  void FileBase::setNonblocking(bool nonblock)  { +    if (m_load_entire_file) { +        throw runtime_error("Cannot set both nonblock and load_entire_file"); +    }      m_nonblock = nonblock;  } -int FileBase::rewind() +void FileBase::setLoadEntireFile(bool load_entire_file)  { -    return ::lseek(m_fd, 0, SEEK_SET); +    if (m_nonblock) { +        throw runtime_error("Cannot set both nonblock and load_entire_file"); +    } +    m_load_entire_file = load_entire_file; +} + +ssize_t FileBase::rewind() +{ +    if (m_load_entire_file) { +        return load_entire_file(); +    } +    else if (m_fd) { +        return ::lseek(m_fd, 0, SEEK_SET); +    } +    else { +        throw runtime_error("Cannot rewind"); +    } +} + +ssize_t FileBase::load_entire_file() +{ +    m_file_contents_offset = 0; +    // Read entire file in chunks of 4MiB +    constexpr size_t blocksize = 4 * 1024 * 1024; +    constexpr int flags = O_RDONLY | O_BINARY; +    m_fd = ::open(m_filename.c_str(), flags); +    if (m_fd == -1) { +        if (not m_file_open_alert_shown) { +            etiLog.level(error) << "Could not open input file " << m_filename << ": " << +                strerror(errno); +        } +        m_file_open_alert_shown = true; +        return -1; +    } + +    // Do not clear the buffer if the file open fails +    m_file_contents.clear(); +    ssize_t offset = 0; +    ssize_t r = 0; +    do { +        m_file_contents.resize(offset + blocksize); +        uint8_t *buffer = m_file_contents.data() + offset; + +        r = read(m_fd, buffer, blocksize); + +        if (r == -1) { +            if (not m_file_open_alert_shown) { +                etiLog.level(error) << "Can't read file " << strerror(errno); +            } +            m_file_contents.clear(); // ensures size is not larger than what we read +            close(); +            m_file_open_alert_shown = true; +            return -1; +        } + +        m_file_contents.resize(offset + r); +        offset += r; +    } while (r > 0); + +    close(); +    if (m_file_open_alert_shown) { +        etiLog.level(info) << "Loaded " << m_file_contents.size() << " bytes from " << m_filename; +    } +    m_file_open_alert_shown = false; +    return m_file_contents.size();  } -ssize_t FileBase::readFromFile(uint8_t* buffer, size_t size) +ssize_t FileBase::readFromFile(uint8_t *buffer, size_t size)  {      using namespace std; @@ -133,7 +207,7 @@ ssize_t FileBase::readFromFile(uint8_t* buffer, size_t size)                  return 0;              }              else if (ret == -1) { -                etiLog.level(alert) << "ERROR: Can't read file " << strerror(errno); +                etiLog.level(error) << "Can't read file " << strerror(errno);                  return -1;              } @@ -156,11 +230,37 @@ ssize_t FileBase::readFromFile(uint8_t* buffer, size_t size)              return 0;          }      } +    else if (m_load_entire_file) { +        // Handle file read errors. +        if (m_file_contents.size() == 0) { +            rewind(); +        } + +        if (m_file_contents.size() == 0) { +            memset(buffer, 0, size); +        } +        else { +            size_t remain = size; + +            while (m_file_contents_offset + remain > m_file_contents.size()) { +                copy(   m_file_contents.begin() + m_file_contents_offset, +                        m_file_contents.end(), buffer); +                size_t copied = m_file_contents.size() - m_file_contents_offset; +                remain -= copied; +                rewind(); +            } + +            copy(   m_file_contents.begin() + m_file_contents_offset, +                    m_file_contents.begin() + m_file_contents_offset + remain, buffer); +            m_file_contents_offset += remain; +        } +        return size; +    }      else {          ret = read(m_fd, buffer, size);          if (ret == -1) { -            etiLog.level(alert) << "ERROR: Can't read file " << strerror(errno); +            etiLog.level(error) << "Can't read file " << strerror(errno);              return -1;          } @@ -168,19 +268,19 @@ ssize_t FileBase::readFromFile(uint8_t* buffer, size_t size)              ssize_t sizeOut = ret;              etiLog.log(info, "reach end of file -> rewinding");              if (rewind() == -1) { -                etiLog.log(alert, "ERROR: Can't rewind file"); +                etiLog.log(error, "Can't rewind file");                  return -1;              }              ret = read(m_fd, buffer + sizeOut, size - sizeOut);              if (ret == -1) { -                etiLog.log(alert, "ERROR: Can't read file"); +                etiLog.log(error, "Can't read file");                  perror("");                  return -1;              }              if (ret < (ssize_t)size) { -                etiLog.log(alert, "ERROR: Not enough data in file"); +                etiLog.log(error, "Not enough data in file");                  return -1;              }          } @@ -228,20 +328,20 @@ READ_SUBCHANNEL:                  goto READ_SUBCHANNEL;              }          case MPEG_FILE_ERROR: -            etiLog.log(alert, "can't read file (%i) -> frame muted", errno); +            etiLog.log(error, "can't read file (%i) -> frame muted", errno);              perror("");              goto MUTE_SUBCHANNEL;          case MPEG_SYNC_NOT_FOUND: -            etiLog.log(alert, "mpeg sync not found, maybe is not a valid file " +            etiLog.log(error, "mpeg sync not found, maybe is not a valid file "                      "-> frame muted");              goto MUTE_SUBCHANNEL;          case MPEG_INVALID_FRAME: -            etiLog.log(alert, "file is not a valid mpeg file " +            etiLog.log(error, "file is not a valid mpeg file "                      "-> frame muted");              goto MUTE_SUBCHANNEL;          default:              if (result < 0) { -                etiLog.log(alert, +                etiLog.log(error,                          "unknown error (code = %i) -> frame muted",                          result);  MUTE_SUBCHANNEL: @@ -275,7 +375,7 @@ MUTE_SUBCHANNEL:                          break;                      default:                          if (result < 0) { -                            etiLog.log(alert, "mpeg file has an invalid DAB " +                            etiLog.log(error, "mpeg file has an invalid DAB "                                      "mpeg frame (unknown reason: %i)", result);                          }                          break; diff --git a/src/input/File.h b/src/input/File.h index 39ce7fd..f317d46 100644 --- a/src/input/File.h +++ b/src/input/File.h @@ -2,7 +2,7 @@     Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2018 Matthias P. Braendli +   Copyright (C) 2022 Matthias P. Braendli      http://www.opendigitalradio.org     */ @@ -43,23 +43,32 @@ class FileBase : public InputBase {          virtual void close();          virtual void setNonblocking(bool nonblock); +        virtual void setLoadEntireFile(bool load_entire_file); +    protected:          /* Rewind the file           * Returns -1 on failure, 0 on success           */ -        virtual int rewind(); -    protected: +        virtual ssize_t rewind();          /* Read len bytes from the file into buf, and return           * the number of bytes read, or -1 in case of error.           */          virtual ssize_t readFromFile(uint8_t* buf, size_t len); +        virtual ssize_t load_entire_file(); +          // We use unix open() instead of fopen() because          // of non-blocking I/O          int m_fd = -1; +        std::string m_filename;          bool m_nonblock = false; +        bool m_load_entire_file = false;          std::vector<uint8_t> m_nonblock_buffer; + +        size_t m_file_contents_offset = 0; +        std::vector<uint8_t> m_file_contents; +        bool m_file_open_alert_shown = false;  };  class MPEGFile : public FileBase { | 
