hls.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*
  2. * Copyright (C) 2018-2020 Stefan Westerfeld
  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, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <string>
  18. #include <regex>
  19. #include <sys/types.h>
  20. #include <sys/wait.h>
  21. #include <sys/stat.h>
  22. #include <unistd.h>
  23. #include "utils.hh"
  24. #include "mpegts.hh"
  25. #include "sfinputstream.hh"
  26. #include "sfoutputstream.hh"
  27. #include "wmcommon.hh"
  28. #include "wavdata.hh"
  29. #include "config.h"
  30. using std::string;
  31. using std::vector;
  32. using std::regex;
  33. using std::map;
  34. using std::min;
  35. #if !HAVE_FFMPEG
  36. int
  37. hls_prepare (const string& in_dir, const string& out_dir, const string& filename, const string& audio_master)
  38. {
  39. error ("audiowmark: hls support is not available in this build of audiowmark\n");
  40. return 1;
  41. }
  42. int
  43. hls_add (const Key& key, const string& infile, const string& outfile, const string& bits)
  44. {
  45. error ("audiowmark: hls support is not available in this build of audiowmark\n");
  46. return 1;
  47. }
  48. #else
  49. #include "hlsoutputstream.hh"
  50. static bool
  51. file_exists (const string& filename)
  52. {
  53. struct stat st;
  54. if (stat (filename.c_str(), &st) == 0)
  55. {
  56. return S_ISREG (st.st_mode);
  57. }
  58. return false;
  59. }
  60. static string
  61. args2string (const vector<string>& args)
  62. {
  63. string result;
  64. bool first = true;
  65. for (auto a : args)
  66. {
  67. if (!first)
  68. result += " ";
  69. first = false;
  70. result += a;
  71. }
  72. return result;
  73. }
  74. static Error
  75. run (const vector<string>& args, vector<string> *pipe_out = nullptr)
  76. {
  77. auto report_error = [=] { error ("audiowmark: failed to execute %s\n", args2string (args).c_str()); };
  78. char *argv[args.size() + 1];
  79. for (size_t i = 0; i < args.size(); i++)
  80. argv[i] = (char *) args[i].c_str();
  81. argv[args.size()] = nullptr;
  82. int pipe_fds[2];
  83. if (pipe_out)
  84. {
  85. if (pipe (pipe_fds) == -1)
  86. {
  87. report_error();
  88. return Error ("pipe() failed");
  89. }
  90. }
  91. pid_t pid = fork();
  92. if (pid < 0)
  93. {
  94. if (pipe_out)
  95. {
  96. close (pipe_fds[0]);
  97. close (pipe_fds[1]);
  98. }
  99. report_error();
  100. return Error ("fork() failed");
  101. }
  102. if (pid == 0) /* child process */
  103. {
  104. if (pipe_out)
  105. {
  106. // replace stdout with pipe
  107. if (dup2 (pipe_fds[1], STDOUT_FILENO) == -1)
  108. {
  109. perror ("audiowmark: dup2() failed");
  110. exit (127);
  111. }
  112. // close remaining pipe fds
  113. close (pipe_fds[0]);
  114. close (pipe_fds[1]);
  115. }
  116. execvp (argv[0], argv);
  117. perror ("audiowmark: execvp() failed");
  118. // should not be reached in normal operation, so exec failed
  119. exit (127);
  120. }
  121. /* parent process */
  122. if (pipe_out) /* capture child stdout */
  123. {
  124. close (pipe_fds[1]); // close pipe write fd
  125. FILE *f = fdopen (pipe_fds[0], "r");
  126. if (!f)
  127. {
  128. close (pipe_fds[0]);
  129. report_error();
  130. return Error ("fdopen() pipe failed");
  131. }
  132. char buffer[1024];
  133. while (fgets (buffer, 1024, f))
  134. {
  135. if (strlen (buffer) && buffer[strlen (buffer) - 1] == '\n')
  136. buffer[strlen (buffer) - 1] = 0;
  137. if (pipe_out)
  138. pipe_out->push_back (buffer);
  139. }
  140. fclose (f); // close pipe read fd
  141. }
  142. int status;
  143. pid_t exited = waitpid (pid, &status, 0);
  144. if (exited < 0)
  145. {
  146. report_error();
  147. return Error ("waitpid() failed");
  148. }
  149. if (WIFEXITED (status))
  150. {
  151. int exit_status = WEXITSTATUS (status);
  152. if (exit_status != 0)
  153. {
  154. report_error();
  155. return Error (string_printf ("subprocess failed / exit status %d", exit_status));
  156. }
  157. }
  158. else
  159. {
  160. report_error();
  161. return Error ("child didn't exit normally");
  162. }
  163. return Error::Code::NONE;
  164. }
  165. Error
  166. ff_decode (const string& filename, WavData& out_wav_data)
  167. {
  168. FILE *tmp_file = tmpfile();
  169. ScopedFile tmp_file_s (tmp_file);
  170. string tmp_file_name = string_printf ("/dev/fd/%d", fileno (tmp_file));
  171. if (!tmp_file)
  172. return Error ("failed to create temp file");
  173. Error err = run ({"ffmpeg", "-v", "error", "-y", "-f", "mpegts", "-i", filename, "-f", "wav", tmp_file_name});
  174. if (err)
  175. return err;
  176. err = out_wav_data.load (tmp_file_name);
  177. return err;
  178. }
  179. int
  180. hls_add (const Key& key, const string& infile, const string& outfile, const string& bits)
  181. {
  182. TSReader reader;
  183. Error err = reader.load (infile);
  184. if (err)
  185. {
  186. error ("hls: %s\n", err.message());
  187. return 1;
  188. }
  189. const TSReader::Entry *full_flac = reader.find ("full.flac");
  190. if (!full_flac)
  191. {
  192. error ("hls: no embedded context found in %s\n", infile.c_str());
  193. return 1;
  194. }
  195. SFInputStream in_stream;
  196. err = in_stream.open (&full_flac->data);
  197. if (err)
  198. {
  199. error ("hls: %s\n", err.message());
  200. return 1;
  201. }
  202. map<string, string> vars = reader.parse_vars ("vars");
  203. bool missing_vars = false;
  204. auto get_var = [&] (const std::string& var) {
  205. auto it = vars.find (var);
  206. if (it == vars.end())
  207. {
  208. error ("audiowmark: hls segment is missing value for required variable '%s'\n", var.c_str());
  209. missing_vars = true;
  210. return "";
  211. }
  212. else
  213. return it->second.c_str();
  214. };
  215. size_t start_pos = atoi (get_var ("start_pos"));
  216. size_t prev_size = atoi (get_var ("prev_size"));
  217. size_t size = atoi (get_var ("size"));
  218. double pts_start = atof (get_var ("pts_start"));
  219. int bit_rate = atoi (get_var ("bit_rate"));
  220. size_t prev_ctx = min<size_t> (1024 * 3, prev_size);
  221. string channel_layout = get_var ("channel_layout");
  222. if (missing_vars)
  223. return 1;
  224. if (Params::hls_bit_rate) // command line option overrides vars bit-rate
  225. bit_rate = Params::hls_bit_rate;
  226. HLSOutputStream out_stream (in_stream.n_channels(), in_stream.sample_rate(), in_stream.bit_depth());
  227. out_stream.set_bit_rate (bit_rate);
  228. out_stream.set_channel_layout (channel_layout);
  229. /* ffmpeg aac encode adds one frame of latency - it would be possible to compensate for this
  230. * by setting shift = 1024, but it can also be done by adjusting the presentation timestamp
  231. */
  232. const size_t shift = 0;
  233. const size_t cut_aac_frames = (prev_ctx + shift) / 1024;
  234. const size_t delete_input_start = prev_size - prev_ctx;
  235. const size_t keep_aac_frames = size / 1024;
  236. err = out_stream.open (outfile, cut_aac_frames, keep_aac_frames, pts_start, delete_input_start);
  237. if (err)
  238. {
  239. error ("audiowmark: error opening HLS output stream %s: %s\n", outfile.c_str(), err.message());
  240. return 1;
  241. }
  242. int wm_rc = add_stream_watermark (key, &in_stream, &out_stream, bits, start_pos - prev_size);
  243. if (wm_rc != 0)
  244. return wm_rc;
  245. info ("AAC Bitrate: %d\n", bit_rate);
  246. return 0;
  247. }
  248. Error
  249. bit_rate_from_m3u8 (const string& m3u8, const WavData& wav_data, int& bit_rate)
  250. {
  251. FILE *tmp_file = tmpfile();
  252. ScopedFile tmp_file_s (tmp_file);
  253. string tmp_file_name = string_printf ("/dev/fd/%d", fileno (tmp_file));
  254. if (!tmp_file)
  255. return Error ("failed to create temp file");
  256. Error err = run ({"ffmpeg", "-v", "error", "-y", "-i", m3u8, "-c:a", "copy", "-f", "adts", tmp_file_name});
  257. if (err)
  258. return err;
  259. struct stat stat_buf;
  260. if (stat (tmp_file_name.c_str(), &stat_buf) != 0)
  261. {
  262. return Error (string_printf ("failed to stat temporary aac file: %s", strerror (errno)));
  263. }
  264. double seconds = double (wav_data.n_frames()) / wav_data.sample_rate();
  265. bit_rate = stat_buf.st_size / seconds * 8;
  266. return Error::Code::NONE;
  267. }
  268. Error
  269. load_audio_master (const string& filename, WavData& audio_master_data)
  270. {
  271. FILE *tmp_file = tmpfile();
  272. ScopedFile tmp_file_s (tmp_file);
  273. string tmp_file_name = string_printf ("/dev/fd/%d", fileno (tmp_file));
  274. if (!tmp_file)
  275. return Error ("failed to create temp file");
  276. /* extract wav */
  277. Error err = run ({"ffmpeg", "-v", "error", "-y", "-i", filename, "-f", "wav", tmp_file_name});
  278. if (err)
  279. return err;
  280. err = audio_master_data.load (tmp_file_name);
  281. if (err)
  282. return err;
  283. return Error::Code::NONE;
  284. }
  285. Error
  286. probe_input_segment (const string& filename, map<string, string>& params)
  287. {
  288. TSReader reader;
  289. Error err = reader.load (filename);
  290. if (err)
  291. {
  292. error ("audiowmark: hls: failed to read mpegts input file: %s\n", filename.c_str());
  293. return err;
  294. }
  295. if (reader.entries().size())
  296. {
  297. error ("audiowmark: hls: file appears to be already prepared: %s\n", filename.c_str());
  298. return Error ("input for hls-prepare must not contain context");
  299. }
  300. vector<string> format_out;
  301. err = run ({"ffprobe", "-v", "error", "-print_format", "compact", "-show_streams", filename}, &format_out);
  302. if (err)
  303. {
  304. error ("audiowmark: hls: failed to validate input file: %s\n", filename.c_str());
  305. return err;
  306. }
  307. for (auto o : format_out)
  308. {
  309. /* parse assignments stream|index=0|codec_name=aac|... */
  310. string key, value;
  311. bool in_key = true;
  312. for (char c : '|' + o + '|')
  313. {
  314. if (c == '=')
  315. {
  316. in_key = false;
  317. }
  318. else if (c == '|')
  319. {
  320. params[key] = value;
  321. in_key = true;
  322. key = "";
  323. value = "";
  324. }
  325. else
  326. {
  327. if (in_key)
  328. key += c;
  329. else
  330. value += c;
  331. }
  332. }
  333. }
  334. return Error::Code::NONE;
  335. }
  336. int
  337. hls_prepare (const string& in_dir, const string& out_dir, const string& filename, const string& audio_master)
  338. {
  339. string in_name = in_dir + "/" + filename;
  340. FILE *in_file = fopen (in_name.c_str(), "r");
  341. ScopedFile in_file_s (in_file);
  342. if (!in_file)
  343. {
  344. error ("audiowmark: error opening input playlist %s\n", in_name.c_str());
  345. return 1;
  346. }
  347. int mkret = mkdir (out_dir.c_str(), 0755);
  348. if (mkret == -1 && errno != EEXIST)
  349. {
  350. error ("audiowmark: unable to create directory %s: %s\n", out_dir.c_str(), strerror (errno));
  351. return 1;
  352. }
  353. string out_name = out_dir + "/" + filename;
  354. if (file_exists (out_name))
  355. {
  356. error ("audiowmark: output file already exists: %s\n", out_name.c_str());
  357. return 1;
  358. }
  359. FILE *out_file = fopen (out_name.c_str(), "w");
  360. ScopedFile out_file_s (out_file);
  361. if (!out_file)
  362. {
  363. error ("audiowmark: error opening output playlist %s\n", out_name.c_str());
  364. return 1;
  365. }
  366. WavData audio_master_data;
  367. Error err = load_audio_master (audio_master, audio_master_data);
  368. if (err)
  369. {
  370. error ("audiowmark: failed to load audio master: %s\n", audio_master.c_str());
  371. return 1;
  372. }
  373. struct Segment
  374. {
  375. string name;
  376. size_t size;
  377. map<string, string> vars;
  378. };
  379. vector<Segment> segments;
  380. char buffer[1024];
  381. const regex blank_re (R"(\s*(#.*)?)");
  382. while (fgets (buffer, 1024, in_file))
  383. {
  384. /* kill newline chars at end */
  385. int last = strlen (buffer) - 1;
  386. while (last > 0 && (buffer[last] == '\n' || buffer[last] == '\r'))
  387. buffer[last--] = 0;
  388. string s = buffer;
  389. std::smatch match;
  390. if (regex_match (s, blank_re))
  391. {
  392. /* blank line or comment */
  393. fprintf (out_file, "%s\n", s.c_str());
  394. }
  395. else
  396. {
  397. fprintf (out_file, "%s\n", s.c_str());
  398. Segment segment;
  399. segment.name = s;
  400. segments.push_back (segment);
  401. }
  402. }
  403. for (auto& segment : segments)
  404. {
  405. map<string, string> params;
  406. string segname = in_dir + "/" + segment.name;
  407. Error err = probe_input_segment (segname, params);
  408. if (err)
  409. {
  410. error ("audiowmark: hls: %s\n", err.message());
  411. return 1;
  412. }
  413. /* validate input segment */
  414. if (atoi (params["index"].c_str()) != 0)
  415. {
  416. error ("audiowmark: hls segment '%s' contains more than one stream\n", segname.c_str());
  417. return 1;
  418. }
  419. if (params["codec_name"] != "aac")
  420. {
  421. error ("audiowmark: hls segment '%s' is not encoded using AAC\n", segname.c_str());
  422. return 1;
  423. }
  424. int segment_channels = atoi (params["channels"].c_str());
  425. if (segment_channels != audio_master_data.n_channels())
  426. {
  427. error ("audiowmark: number of channels mismatch:\n - hls segment '%s' has %d channels\n - audio master '%s' has %d channels\n",
  428. segname.c_str(), segment_channels, audio_master.c_str(), audio_master_data.n_channels());
  429. return 1;
  430. }
  431. /* get segment parameters */
  432. if (params["channel_layout"].empty())
  433. {
  434. error ("audiowmark: hls segment '%s' has no channel_layout entry\n", segname.c_str());
  435. return 1;
  436. }
  437. segment.vars["channel_layout"] = params["channel_layout"];
  438. /* get start pts */
  439. if (params["start_time"].empty())
  440. {
  441. error ("audiowmark: hls segment '%s' has no start_time entry\n", segname.c_str());
  442. return 1;
  443. }
  444. segment.vars["pts_start"] = params["start_time"];
  445. }
  446. /* find bitrate for AAC encoder */
  447. int bit_rate = 0;
  448. if (!Params::hls_bit_rate)
  449. {
  450. err = bit_rate_from_m3u8 (in_dir + "/" + filename, audio_master_data, bit_rate);
  451. if (err)
  452. {
  453. error ("audiowmark: bit-rate detection failed: %s\n", err.message());
  454. return 1;
  455. }
  456. info ("AAC Bitrate: %d (detected)\n", bit_rate);
  457. }
  458. else
  459. {
  460. bit_rate = Params::hls_bit_rate;
  461. info ("AAC Bitrate: %d\n", bit_rate);
  462. }
  463. info ("Segments: %zd\n", segments.size());
  464. size_t start_pos = 0;
  465. for (auto& segment : segments)
  466. {
  467. WavData out;
  468. Error err = ff_decode (in_dir + "/" + segment.name, out);
  469. if (err)
  470. {
  471. error ("audiowmark: hls: ff_decode failed: %s\n", err.message());
  472. return 1;
  473. }
  474. segment.size = out.n_values() / out.n_channels();
  475. if ((segment.size % 1024) != 0)
  476. {
  477. error ("audiowmark: hls input segments need 1024-sample alignment (due to AAC)\n");
  478. return 1;
  479. }
  480. /* store 3 seconds of the context before this segment and after this segment (if available) */
  481. const size_t ctx_3sec = 3 * out.sample_rate();
  482. const size_t prev_size = min<size_t> (start_pos, ctx_3sec);
  483. const size_t segment_size_with_ctx = prev_size + segment.size + ctx_3sec;
  484. segment.vars["start_pos"] = string_printf ("%zd", start_pos);
  485. segment.vars["size"] = string_printf ("%zd", segment.size);
  486. segment.vars["prev_size"] = string_printf ("%zd", prev_size);
  487. segment.vars["bit_rate"] = string_printf ("%d", bit_rate);
  488. /* write audio segment with context */
  489. const size_t start_point = min (start_pos - prev_size, audio_master_data.n_frames());
  490. const size_t end_point = min (start_point + segment_size_with_ctx, audio_master_data.n_frames());
  491. vector<float> out_signal (audio_master_data.samples().begin() + start_point * audio_master_data.n_channels(),
  492. audio_master_data.samples().begin() + end_point * audio_master_data.n_channels());
  493. // append zeros if audio master is too short to provide segment with context
  494. out_signal.resize (segment_size_with_ctx * audio_master_data.n_channels());
  495. vector<unsigned char> full_flac_mem;
  496. SFOutputStream out_stream;
  497. err = out_stream.open (&full_flac_mem,
  498. audio_master_data.n_channels(), audio_master_data.sample_rate(), audio_master_data.bit_depth(),
  499. SFOutputStream::OutFormat::FLAC);
  500. if (err)
  501. {
  502. error ("audiowmark: hls: open context flac failed: %s\n", err.message());
  503. return 1;
  504. }
  505. err = out_stream.write_frames (out_signal);
  506. if (err)
  507. {
  508. error ("audiowmark: hls: write context flac failed: %s\n", err.message());
  509. return 1;
  510. }
  511. err = out_stream.close();
  512. if (err)
  513. {
  514. error ("audiowmark: hls: close context flac failed: %s\n", err.message());
  515. return 1;
  516. }
  517. /* store everything we need in a mpegts file */
  518. TSWriter writer;
  519. writer.append_data ("full.flac", full_flac_mem);
  520. writer.append_vars ("vars", segment.vars);
  521. string out_segment = out_dir + "/" + segment.name;
  522. if (file_exists (out_segment))
  523. {
  524. error ("audiowmark: output file already exists: %s\n", out_segment.c_str());
  525. return 1;
  526. }
  527. err = writer.process (in_dir + "/" + segment.name, out_segment);
  528. if (err)
  529. {
  530. error ("audiowmark: processing hls segment %s failed: %s\n", segment.name.c_str(), err.message());
  531. return 1;
  532. }
  533. /* start position for the next segment */
  534. start_pos += segment.size;
  535. }
  536. int orig_seconds = start_pos / audio_master_data.sample_rate();
  537. info ("Time: %d:%02d\n", orig_seconds / 60, orig_seconds % 60);
  538. return 0;
  539. }
  540. #endif