1 /*
2 mkvmerge -- utility for splicing together matroska files
3 from component media subtypes
4
5 Distributed under the GPL v2
6 see the file COPYING for details
7 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8
9 helper functions for SPU data (SubPicture Units — subtitles on DVDs)
10
11 Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13
14 #include "common/common_pch.h"
15
16 #include "common/endian.h"
17 #include "common/spu.h"
18 #include "common/strings/formatting.h"
19
20 namespace mtx::spu {
21
22 static std::optional<std::size_t>
find_stop_display_position(unsigned char const * data,std::size_t const buf_size)23 find_stop_display_position(unsigned char const *data,
24 std::size_t const buf_size) {
25 static debugging_option_c debug{"spu|spu_find_stop_display_position"};
26
27 if (buf_size < 4)
28 return {};
29
30 uint32_t control_start = get_uint16_be(&data[2]);
31 uint32_t start_off = 0;
32 auto next_off = control_start;
33
34 while ((start_off != next_off) && (next_off < buf_size)) {
35 start_off = next_off;
36 next_off = get_uint16_be(&data[start_off + 2]);
37
38 if (next_off < start_off) {
39 mxdebug_if(debug, "spu::extraction_duration: Encountered broken SPU packet (next_off < start_off)\n");
40 return {};
41 }
42
43 auto off = start_off + 4;
44 for (auto type = data[off++]; type != 0xff; type = data[off++]) {
45 auto info = fmt::format("spu_extraction_duration: cmd = {0:02x} ", static_cast<unsigned int>(type));
46 auto unknown = false;
47 switch(type) {
48 case 0x00:
49 // Menu ID, 1 byte
50 info += "menu ID";
51 break;
52 case 0x01:
53 // Start display
54 info += "start display";
55 break;
56 case 0x02: {
57 // Stop display
58 auto date = timestamp_c::mpeg(static_cast<int64_t>(get_uint16_be(&data[start_off])) * 1024);
59 info += fmt::format("stop display: {0}\n", mtx::string::format_timestamp(date));
60 mxdebug_if(debug, info);
61 return start_off;
62 }
63 case 0x03:
64 // Palette
65 info += "palette";
66 off += 2;
67 break;
68 case 0x04:
69 // Alpha
70 info += "alpha";
71 off += 2;
72 break;
73 case 0x05:
74 info += "coords";
75 off += 6;
76 break;
77 case 0x06:
78 info += "graphic lines";
79 off += 4;
80 break;
81 case 0xff:
82 // All done, bye-bye
83 info += "done";
84 return {};
85 default:
86 info += fmt::format("unknown (0x{0:02x}), skipping {1} bytes.", type, next_off - off);
87 unknown = true;
88 }
89 mxdebug_if(debug, fmt::format("{0}\n", info));
90 if (unknown)
91 break;
92 }
93 }
94
95 return {};
96 }
97
98 timestamp_c
get_duration(unsigned char const * data,std::size_t const buf_size)99 get_duration(unsigned char const *data,
100 std::size_t const buf_size) {
101 auto position = find_stop_display_position(data, buf_size);
102 if (!position || ((*position + 2) > buf_size))
103 return {};
104
105 return timestamp_c::mpeg(static_cast<int64_t>(get_uint16_be(&data[*position])) * 1024);
106 }
107
108 void
set_duration(unsigned char * data,std::size_t const buf_size,timestamp_c const & duration)109 set_duration(unsigned char *data,
110 std::size_t const buf_size,
111 timestamp_c const &duration) {
112 auto position = find_stop_display_position(data, buf_size);
113
114 if (position && ((*position + 2) <= buf_size))
115 put_uint16_be(&data[*position], duration.to_mpeg() / 1024);
116 }
117
118 }
119