/* Copyright (C) 2014 CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/) Copyright (C) 2014, 2015 Matthias P. Braendli (http://opendigitalradio.org) Copyright (C) 2015, 2016, 2017 Stefan Pöschel (http://opendigitalradio.org) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /*! \file odr-padenc.cpp \brief Generete PAD data for MOT Slideshow and DLS \author Sergio Sagliocco \author Matthias P. Braendli \author Stefan Pöschel */ #include "common.h" #include #include #include #include #include #include #include #include #include "pad_common.h" #include "dls.h" #include "sls.h" static const int SLEEPDELAY_DEFAULT = 10; // seconds void usage(char* name) { fprintf(stderr, "DAB PAD encoder %s for MOT Slideshow and DLS\n\n" "By CSP Innovazione nelle ICT s.c.a r.l. (http://rd.csp.it/) and\n" "Opendigitalradio.org\n\n" "Reads image data from the specified directory, DLS text from a file,\n" "and outputs PAD data to the given FIFO.\n" " http://opendigitalradio.org\n\n", #if defined(GITVERSION) GITVERSION #else PACKAGE_VERSION #endif ); fprintf(stderr, "Usage: %s [OPTIONS...]\n", name); fprintf(stderr, " -d, --dir=DIRNAME Directory to read images from.\n" " -e, --erase Erase slides from DIRNAME once they have\n" " been encoded.\n" " -s, --sleep=DELAY Wait DELAY seconds between each slide\n" " Default: %d\n" " -o, --output=FILENAME FIFO to write PAD data into.\n" " Default: /tmp/pad.fifo\n" " -t, --dls=FILENAME FIFO or file to read DLS text from.\n" " If specified more than once, use next file after DELAY seconds.\n" " -p, --pad=LENGTH Set the pad length.\n" " Possible values: %s\n" " Default: 58\n" " -c, --charset=ID ID of the character set encoding used for DLS text input.\n" " ID = 0: Complete EBU Latin based repertoire\n" " ID = 6: ISO/IEC 10646 using UCS-2 BE\n" " ID = 15: ISO/IEC 10646 using UTF-8\n" " Default: 15\n" " -r, --remove-dls Always insert a DLS Remove Label command when replacing a DLS text.\n" " -C, --raw-dls Do not convert DLS texts to Complete EBU Latin based repertoire\n" " character set encoding.\n" " -R, --raw-slides Do not process slides. Integrity checks and resizing\n" " slides is skipped. Use this if you know what you are doing !\n" " It is useful only when -d is used\n" " -v, --verbose Print more information to the console\n", SLEEPDELAY_DEFAULT, PADPacketizer::ALLOWED_PADLEN.c_str() ); } std::string list_dls_files(std::vector dls_files) { std::string result = ""; for(std::string dls_file : dls_files) { if(!result.empty()) result += "/"; result += "'" + dls_file + "'"; } return result; } int main(int argc, char *argv[]) { int ret; struct dirent *pDirent; size_t padlen = 58; bool erase_after_tx = false; int sleepdelay = SLEEPDELAY_DEFAULT; bool raw_slides = false; DABCharset charset = DABCharset::UTF8; bool raw_dls = false; bool remove_dls = false; const char* sls_dir = NULL; const char* output = "/tmp/pad.fifo"; std::vector dls_files; int curr_dls_file = 0; const struct option longopts[] = { {"charset", required_argument, 0, 'c'}, {"raw-dls", no_argument, 0, 'C'}, {"remove-dls", no_argument, 0, 'r'}, {"dir", required_argument, 0, 'd'}, {"erase", no_argument, 0, 'e'}, {"output", required_argument, 0, 'o'}, {"dls", required_argument, 0, 't'}, {"pad", required_argument, 0, 'p'}, {"sleep", required_argument, 0, 's'}, {"raw-slides", no_argument, 0, 'R'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {0,0,0,0}, }; int ch=0; int index; while(ch != -1) { ch = getopt_long(argc, argv, "eChRrc:d:o:p:s:t:v", longopts, &index); switch (ch) { case 'c': charset = (DABCharset) atoi(optarg); break; case 'C': raw_dls = true; break; case 'r': remove_dls = true; break; case 'd': sls_dir = optarg; break; case 'e': erase_after_tx = true; break; case 'o': output = optarg; break; case 's': sleepdelay = atoi(optarg); break; case 't': dls_files.push_back(optarg); break; case 'p': padlen = atoi(optarg); break; case 'R': raw_slides = true; break; case 'v': verbose++; break; case '?': case 'h': usage(argv[0]); return 0; } } if (padlen != PADPacketizer::SHORT_PAD && (padlen < PADPacketizer::VARSIZE_PAD_MIN || padlen > PADPacketizer::VARSIZE_PAD_MAX)) { fprintf(stderr, "ODR-PadEnc Error: pad length %zu invalid: Possible values: %s\n", padlen, PADPacketizer::ALLOWED_PADLEN.c_str()); return 2; } if (sls_dir && not dls_files.empty()) { fprintf(stderr, "ODR-PadEnc encoding Slideshow from '%s' and DLS from %s to '%s'\n", sls_dir, list_dls_files(dls_files).c_str(), output); } else if (sls_dir) { fprintf(stderr, "ODR-PadEnc encoding Slideshow from '%s' to '%s'. No DLS.\n", sls_dir, output); } else if (not dls_files.empty()) { fprintf(stderr, "ODR-PadEnc encoding DLS from %s to '%s'. No Slideshow.\n", list_dls_files(dls_files).c_str(), output); } else { fprintf(stderr, "ODR-PadEnc Error: No DLS nor slideshow to encode !\n"); usage(argv[0]); return 1; } const char* user_charset; switch (charset) { case DABCharset::COMPLETE_EBU_LATIN: user_charset = "Complete EBU Latin"; break; case DABCharset::EBU_LATIN_CY_GR: user_charset = "EBU Latin core, Cyrillic, Greek"; break; case DABCharset::EBU_LATIN_AR_HE_CY_GR: user_charset = "EBU Latin core, Arabic, Hebrew, Cyrillic, Greek"; break; case DABCharset::ISO_LATIN_ALPHABET_2: user_charset = "ISO Latin Alphabet 2"; break; case DABCharset::UCS2_BE: user_charset = "UCS-2 BE"; break; case DABCharset::UTF8: user_charset = "UTF-8"; break; default: fprintf(stderr, "ODR-PadEnc Error: Invalid charset!\n"); usage(argv[0]); return 1; } fprintf(stderr, "ODR-PadEnc using charset %s (%d)\n", user_charset, charset); if (not raw_dls) { switch (charset) { case DABCharset::COMPLETE_EBU_LATIN: // no conversion needed break; case DABCharset::UTF8: fprintf(stderr, "ODR-PadEnc converting DLS texts to Complete EBU Latin\n"); break; default: fprintf(stderr, "ODR-PadEnc Error: DLS conversion to EBU is currently only supported for UTF-8 input!\n"); return 1; } } int output_fd = open(output, O_WRONLY); if (output_fd == -1) { perror("ODR-PadEnc Error: failed to open output"); return 3; } #if HAVE_MAGICKWAND MagickWandGenesis(); #endif PADPacketizer pad_packetizer(padlen); DLSManager dls_manager(&pad_packetizer); SLSManager sls_manager(&pad_packetizer); std::list slides_to_transmit; History slides_history(History::MAXHISTORYLEN); while(1) { if (sls_dir) { // slide + possibly DLS DIR *pDir = opendir(sls_dir); if (pDir == NULL) { fprintf(stderr, "ODR-PadEnc Error: cannot open directory '%s'\n", sls_dir); return 1; } // Add new slides to transmit to list while ((pDirent = readdir(pDir)) != NULL) { std::string slide = pDirent->d_name; // skip dirs beginning with '.' if(slide[0] == '.') continue; // skip slide params files if(slide.length() >= SLSManager::SLS_PARAMS_SUFFIX.length() && slide.compare(slide.length() - SLSManager::SLS_PARAMS_SUFFIX.length(), SLSManager::SLS_PARAMS_SUFFIX.length(), SLSManager::SLS_PARAMS_SUFFIX) == 0) continue; // add slide char imagepath[256]; sprintf(imagepath, "%s/%s", sls_dir, slide.c_str()); slide_metadata_t md; md.filepath = imagepath; md.fidx = slides_history.get_fidx(imagepath); slides_to_transmit.push_back(md); if (verbose) { fprintf(stderr, "ODR-PadEnc found slide '%s', fidx %d\n", imagepath, md.fidx); } } closedir(pDir); #ifdef DEBUG slides_history.disp_database(); #endif // if ATM no slides, transmit at least DLS if (slides_to_transmit.empty()) { if (not dls_files.empty()) { dls_manager.writeDLS(dls_files[curr_dls_file], charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd); curr_dls_file = (curr_dls_file + 1) % dls_files.size(); } sleep(sleepdelay); } else { // Sort the list in fidx order slides_to_transmit.sort(); // Encode the slides for (std::list::const_iterator it = slides_to_transmit.cbegin(); it != slides_to_transmit.cend(); ++it) { ret = sls_manager.encodeFile(it->filepath, it->fidx, raw_slides); if (ret != 1) fprintf(stderr, "ODR-PadEnc Error: cannot encode file '%s'\n", it->filepath.c_str()); if (erase_after_tx) { if (unlink(it->filepath.c_str()) == -1) { fprintf(stderr, "ODR-PadEnc Error: erasing file '%s' failed: ", it->filepath.c_str()); perror(""); } } // while flushing, insert DLS after a certain PAD amout while (pad_packetizer.QueueFilled()) { if (not dls_files.empty()) dls_manager.writeDLS(dls_files[curr_dls_file], charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd, DLSManager::DLS_REPETITION_WHILE_SLS); } // after the slide, output a last DLS if (not dls_files.empty()) dls_manager.writeDLS(dls_files[curr_dls_file], charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd); curr_dls_file = (curr_dls_file + 1) % dls_files.size(); sleep(sleepdelay); } slides_to_transmit.resize(0); } } else { // only DLS // Always retransmit DLS, we want it to be updated frequently dls_manager.writeDLS(dls_files[curr_dls_file], charset, raw_dls, remove_dls); pad_packetizer.WriteAllPADs(output_fd); curr_dls_file = (curr_dls_file + 1) % dls_files.size(); sleep(sleepdelay); } } return 1; }