1 /*
2 mkvextract -- extract tracks from Matroska files into other files
3
4 Distributed under the GPL v2
5 see the file COPYING for details
6 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
7
8 Extraction of Blu-Ray text subtitles.
9
10 Written by Moritz Bunkus and Mike Chen.
11 */
12
13 #include "common/common_pch.h"
14
15 #include <algorithm>
16 #include <matroska/KaxBlock.h>
17
18 #include "common/ebml.h"
19 #include "common/endian.h"
20 #include "common/hdmv_textst.h"
21 #include "extract/xtr_hdmv_textst.h"
22
xtr_hdmv_textst_c(const std::string & codec_id,int64_t tid,track_spec_t & tspec)23 xtr_hdmv_textst_c::xtr_hdmv_textst_c(const std::string &codec_id,
24 int64_t tid,
25 track_spec_t &tspec)
26 : xtr_base_c{codec_id, tid, tspec}
27 , m_num_presentation_segments{}
28 , m_num_presentation_segment_position{}
29 {
30 }
31
32 void
create_file(xtr_base_c * master,libmatroska::KaxTrackEntry & track)33 xtr_hdmv_textst_c::create_file(xtr_base_c *master,
34 libmatroska::KaxTrackEntry &track) {
35 xtr_base_c::create_file(master, track);
36
37 auto priv = FindChild<libmatroska::KaxCodecPrivate>(&track);
38 if (!priv)
39 mxerror(fmt::format(Y("Track {0} with the CodecID '{1}' is missing the \"codec private\" element and cannot be extracted.\n"), m_tid, m_codec_id));
40
41 auto mpriv = decode_codec_private(priv);
42
43 if (mpriv->get_size() < 6)
44 mxerror(fmt::format(Y("Track {0} CodecPrivate is too small.\n"), m_tid));
45
46 // Older files created by MakeMKV have a different layout:
47 // 1 byte language code
48 // segment descriptor:
49 // 1 byte segment type (dialog style segment)
50 // 2 bytes segment size
51 // x bytes dialog style segment data
52 // 2 bytes frame count
53
54 // Newer files will only contain the dialog style segment's
55 // descriptor and data
56
57 auto buf = mpriv->get_buffer();
58 auto old_style = buf[0] && (buf[0] < 0x10);
59 auto style_segment_start = old_style ? 1 : 0;
60 auto style_segment_size = get_uint16_be(&buf[style_segment_start + 1]);
61
62 if (mpriv->get_size() < static_cast<uint64_t>(3 + style_segment_size + (old_style ? 1 + 2 : 0)))
63 mxerror(fmt::format(Y("Track {0} CodecPrivate is too small.\n"), m_tid));
64
65 m_out->write("TextST"s);
66
67 // The dialog style segment.
68 m_out->write(&buf[style_segment_start], 3 + style_segment_size);
69
70 // Remember the position for fixing the number of presentation
71 // segments.
72 unsigned char const zero[2] = { 0, 0 };
73 m_num_presentation_segment_position = m_out->getFilePointer();
74 m_out->write(&zero[0], 2);
75 }
76
77 void
finish_file()78 xtr_hdmv_textst_c::finish_file() {
79 unsigned char buf[2];
80
81 put_uint16_be(&buf[0], m_num_presentation_segments);
82 m_out->setFilePointer(m_num_presentation_segment_position);
83 m_out->write(&buf[0], 2);
84 }
85
86 void
handle_frame(xtr_frame_t & f)87 xtr_hdmv_textst_c::handle_frame(xtr_frame_t &f) {
88 if (f.frame->get_size() < 13)
89 return;
90
91 ++m_num_presentation_segments;
92
93 auto buf = f.frame->get_buffer();
94 auto start_pts = timestamp_c::ns(f.timestamp);
95 auto duration = std::max<int64_t>(f.duration, 0);
96 auto end_pts = start_pts + (duration != 0 ? timestamp_c::ns(duration) : (mtx::hdmv_textst::get_timestamp(&buf[3 + 5]) - mtx::hdmv_textst::get_timestamp(&buf[3 + 0])).abs());
97
98 mtx::hdmv_textst::put_timestamp(&buf[3 + 0], start_pts);
99 mtx::hdmv_textst::put_timestamp(&buf[3 + 5], end_pts);
100
101 m_out->write(f.frame);
102 }
103