/src/ffmpeg/libavformat/s337m.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2017 foo86 |
3 | | * |
4 | | * This file is part of FFmpeg. |
5 | | * |
6 | | * FFmpeg is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * FFmpeg is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with FFmpeg; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #include "libavutil/intreadwrite.h" |
22 | | #include "avformat.h" |
23 | | #include "demux.h" |
24 | | #include "internal.h" |
25 | | #include "spdif.h" |
26 | | |
27 | 1.38G | #define MARKER_16LE 0x72F81F4E |
28 | 1.38G | #define MARKER_20LE 0x20876FF0E154 |
29 | 1.38G | #define MARKER_24LE 0x72F8961F4EA5 |
30 | | |
31 | 2.76G | #define IS_16LE_MARKER(state) ((state & 0xFFFFFFFF) == MARKER_16LE) |
32 | 2.76G | #define IS_20LE_MARKER(state) ((state & 0xF0FFFFF0FFFF) == MARKER_20LE) |
33 | 1.38G | #define IS_24LE_MARKER(state) ((state & 0xFFFFFFFFFFFF) == MARKER_24LE) |
34 | 1.38G | #define IS_LE_MARKER(state) (IS_16LE_MARKER(state) || IS_20LE_MARKER(state) || IS_24LE_MARKER(state)) |
35 | | |
36 | | static int s337m_get_offset_and_codec(void *avc, |
37 | | uint64_t state, |
38 | | int data_type, int data_size, |
39 | | int *offset, enum AVCodecID *codec) |
40 | 299k | { |
41 | 299k | int word_bits; |
42 | | |
43 | 299k | if (IS_16LE_MARKER(state)) { |
44 | 276k | word_bits = 16; |
45 | 276k | } else if (IS_20LE_MARKER(state)) { |
46 | 4.46k | data_type >>= 8; |
47 | 4.46k | data_size >>= 4; |
48 | 4.46k | word_bits = 20; |
49 | 18.8k | } else { |
50 | 18.8k | data_type >>= 8; |
51 | 18.8k | word_bits = 24; |
52 | 18.8k | } |
53 | | |
54 | 299k | if ((data_type & 0x1F) != 0x1C) { |
55 | 278k | if (avc) |
56 | 103 | avpriv_report_missing_feature(avc, "Data type %#x in SMPTE 337M", data_type & 0x1F); |
57 | 278k | return AVERROR_PATCHWELCOME; |
58 | 278k | } |
59 | | |
60 | 20.6k | if (codec) |
61 | 4.36k | *codec = AV_CODEC_ID_DOLBY_E; |
62 | | |
63 | 20.6k | switch (data_size / word_bits) { |
64 | 2.22k | case 3648: |
65 | 2.22k | *offset = 1920; |
66 | 2.22k | break; |
67 | 810 | case 3644: |
68 | 810 | *offset = 2002; |
69 | 810 | break; |
70 | 787 | case 3640: |
71 | 787 | *offset = 2000; |
72 | 787 | break; |
73 | 7.13k | case 3040: |
74 | 7.13k | *offset = 1601; |
75 | 7.13k | break; |
76 | 9.67k | default: |
77 | 9.67k | if (avc) |
78 | 80 | avpriv_report_missing_feature(avc, "Dolby E data size %d in SMPTE 337M", data_size); |
79 | 9.67k | return AVERROR_PATCHWELCOME; |
80 | 20.6k | } |
81 | | |
82 | 10.9k | *offset -= 4; |
83 | 10.9k | *offset *= (word_bits + 7 >> 3) * 2; |
84 | 10.9k | return 0; |
85 | 20.6k | } |
86 | | |
87 | | static int s337m_probe(const AVProbeData *p) |
88 | 358k | { |
89 | 358k | uint64_t state = 0; |
90 | 358k | int markers[3] = { 0 }; |
91 | 358k | int i, pos, sum, max, data_type, data_size, offset; |
92 | 358k | uint8_t *buf; |
93 | | |
94 | 1.37G | for (pos = 0; pos < p->buf_size; pos++) { |
95 | 1.37G | state = (state << 8) | p->buf[pos]; |
96 | 1.37G | if (!IS_LE_MARKER(state)) |
97 | 1.37G | continue; |
98 | | |
99 | 294k | buf = p->buf + pos + 1; |
100 | 294k | if (IS_16LE_MARKER(state)) { |
101 | 271k | data_type = AV_RL16(buf ); |
102 | 271k | data_size = AV_RL16(buf + 2); |
103 | 271k | } else { |
104 | 23.0k | data_type = AV_RL24(buf ); |
105 | 23.0k | data_size = AV_RL24(buf + 3); |
106 | 23.0k | } |
107 | | |
108 | 294k | if (s337m_get_offset_and_codec(NULL, state, data_type, data_size, &offset, NULL)) |
109 | 288k | continue; |
110 | | |
111 | 6.67k | i = IS_16LE_MARKER(state) ? 0 : IS_20LE_MARKER(state) ? 1 : 2; |
112 | 6.67k | markers[i]++; |
113 | | |
114 | 6.67k | pos += IS_16LE_MARKER(state) ? 4 : 6; |
115 | 6.67k | pos += offset; |
116 | 6.67k | state = 0; |
117 | 6.67k | } |
118 | | |
119 | 358k | sum = max = 0; |
120 | 1.43M | for (i = 0; i < FF_ARRAY_ELEMS(markers); i++) { |
121 | 1.07M | sum += markers[i]; |
122 | 1.07M | if (markers[max] < markers[i]) |
123 | 148 | max = i; |
124 | 1.07M | } |
125 | | |
126 | 358k | if (markers[max] > 3 && markers[max] * 4 > sum * 3) |
127 | 324 | return AVPROBE_SCORE_EXTENSION + 1; |
128 | | |
129 | 358k | return 0; |
130 | 358k | } |
131 | | |
132 | | static int s337m_read_header(AVFormatContext *s) |
133 | 5.27k | { |
134 | 5.27k | s->ctx_flags |= AVFMTCTX_NOHEADER; |
135 | 5.27k | return 0; |
136 | 5.27k | } |
137 | | |
138 | | static void bswap_buf24(uint8_t *data, int size) |
139 | 203 | { |
140 | 203 | int i; |
141 | | |
142 | 670k | for (i = 0; i < size / 3; i++, data += 3) |
143 | 670k | FFSWAP(uint8_t, data[0], data[2]); |
144 | 203 | } |
145 | | |
146 | | static int s337m_read_packet(AVFormatContext *s, AVPacket *pkt) |
147 | 14.6k | { |
148 | 14.6k | AVIOContext *pb = s->pb; |
149 | 14.6k | uint64_t state = 0; |
150 | 14.6k | int ret, data_type, data_size, offset; |
151 | 14.6k | enum AVCodecID codec; |
152 | | |
153 | 8.36M | while (!IS_LE_MARKER(state)) { |
154 | 8.35M | state = (state << 8) | avio_r8(pb); |
155 | 8.35M | if (avio_feof(pb)) |
156 | 10.1k | return AVERROR_EOF; |
157 | 8.35M | } |
158 | | |
159 | 4.46k | if (IS_16LE_MARKER(state)) { |
160 | 4.17k | data_type = avio_rl16(pb); |
161 | 4.17k | data_size = avio_rl16(pb); |
162 | 4.17k | } else { |
163 | 289 | data_type = avio_rl24(pb); |
164 | 289 | data_size = avio_rl24(pb); |
165 | 289 | } |
166 | | |
167 | 4.46k | if ((ret = s337m_get_offset_and_codec(s, state, data_type, data_size, &offset, &codec)) < 0) |
168 | 183 | return ret; |
169 | | |
170 | 4.28k | if ((ret = av_get_packet(pb, pkt, offset)) != offset) |
171 | 88 | return ret < 0 ? ret : AVERROR_EOF; |
172 | | |
173 | 4.19k | if (IS_16LE_MARKER(state)) |
174 | 3.99k | ff_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1); |
175 | 203 | else |
176 | 203 | bswap_buf24(pkt->data, pkt->size); |
177 | | |
178 | 4.19k | if (!s->nb_streams) { |
179 | 161 | AVStream *st = avformat_new_stream(s, NULL); |
180 | 161 | if (!st) { |
181 | 0 | return AVERROR(ENOMEM); |
182 | 0 | } |
183 | 161 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
184 | 161 | st->codecpar->codec_id = codec; |
185 | 161 | ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS; |
186 | 161 | } |
187 | | |
188 | 4.19k | return 0; |
189 | 4.19k | } |
190 | | |
191 | | const FFInputFormat ff_s337m_demuxer = { |
192 | | .p.name = "s337m", |
193 | | .p.long_name = NULL_IF_CONFIG_SMALL("SMPTE 337M"), |
194 | | .p.flags = AVFMT_GENERIC_INDEX, |
195 | | .read_probe = s337m_probe, |
196 | | .read_header = s337m_read_header, |
197 | | .read_packet = s337m_read_packet, |
198 | | }; |