]> git.sesse.net Git - ffmpeg/blob - libavformat/s337m.c
9d1b52eb614bcd1e1bd120eea5235fa8af64b27d
[ffmpeg] / libavformat / s337m.c
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 "spdif.h"
24
25 #define MARKER_16LE         0x72F81F4E
26 #define MARKER_20LE         0x20876FF0E154
27 #define MARKER_24LE         0x72F8961F4EA5
28
29 #define IS_16LE_MARKER(state)   ((state & 0xFFFFFFFF) == MARKER_16LE)
30 #define IS_20LE_MARKER(state)   ((state & 0xF0FFFFF0FFFF) == MARKER_20LE)
31 #define IS_24LE_MARKER(state)   ((state & 0xFFFFFFFFFFFF) == MARKER_24LE)
32 #define IS_LE_MARKER(state)     (IS_16LE_MARKER(state) || IS_20LE_MARKER(state) || IS_24LE_MARKER(state))
33
34 static int s337m_get_offset_and_codec(void *avc,
35                                       uint64_t state,
36                                       int data_type, int data_size,
37                                       int *offset, enum AVCodecID *codec)
38 {
39     int word_bits;
40
41     if (IS_16LE_MARKER(state)) {
42         word_bits = 16;
43     } else if (IS_20LE_MARKER(state)) {
44         data_type >>= 8;
45         data_size >>= 4;
46         word_bits = 20;
47     } else {
48         data_type >>= 8;
49         word_bits = 24;
50     }
51
52     if ((data_type & 0x1F) != 0x1C) {
53         if (avc)
54             avpriv_report_missing_feature(avc, "Data type %#x in SMPTE 337M", data_type & 0x1F);
55         return AVERROR_PATCHWELCOME;
56     }
57
58     if (codec)
59         *codec = AV_CODEC_ID_DOLBY_E;
60
61     switch (data_size / word_bits) {
62     case 3648:
63         *offset = 1920;
64         break;
65     case 3644:
66         *offset = 2002;
67         break;
68     case 3640:
69         *offset = 2000;
70         break;
71     case 3040:
72         *offset = 1601;
73         break;
74     default:
75         if (avc)
76             avpriv_report_missing_feature(avc, "Dolby E data size %d in SMPTE 337M", data_size);
77         return AVERROR_PATCHWELCOME;
78     }
79
80     *offset -= 4;
81     *offset *= (word_bits + 7 >> 3) * 2;
82     return 0;
83 }
84
85 static int s337m_probe(const AVProbeData *p)
86 {
87     uint64_t state = 0;
88     int markers[3] = { 0 };
89     int i, pos, sum, max, data_type, data_size, offset;
90     uint8_t *buf;
91
92     for (pos = 0; pos < p->buf_size; pos++) {
93         state = (state << 8) | p->buf[pos];
94         if (!IS_LE_MARKER(state))
95             continue;
96
97         buf = p->buf + pos + 1;
98         if (IS_16LE_MARKER(state)) {
99             data_type = AV_RL16(buf    );
100             data_size = AV_RL16(buf + 2);
101         } else {
102             data_type = AV_RL24(buf    );
103             data_size = AV_RL24(buf + 3);
104         }
105
106         if (s337m_get_offset_and_codec(NULL, state, data_type, data_size, &offset, NULL))
107             continue;
108
109         i = IS_16LE_MARKER(state) ? 0 : IS_20LE_MARKER(state) ? 1 : 2;
110         markers[i]++;
111
112         pos  += IS_16LE_MARKER(state) ? 4 : 6;
113         pos  += offset;
114         state = 0;
115     }
116
117     sum = max = 0;
118     for (i = 0; i < FF_ARRAY_ELEMS(markers); i++) {
119         sum += markers[i];
120         if (markers[max] < markers[i])
121             max = i;
122     }
123
124     if (markers[max] > 3 && markers[max] * 4 > sum * 3)
125         return AVPROBE_SCORE_EXTENSION + 1;
126
127     return 0;
128 }
129
130 static int s337m_read_header(AVFormatContext *s)
131 {
132     s->ctx_flags |= AVFMTCTX_NOHEADER;
133     return 0;
134 }
135
136 static void bswap_buf24(uint8_t *data, int size)
137 {
138     int i;
139
140     for (i = 0; i < size / 3; i++, data += 3)
141         FFSWAP(uint8_t, data[0], data[2]);
142 }
143
144 static int s337m_read_packet(AVFormatContext *s, AVPacket *pkt)
145 {
146     AVIOContext *pb = s->pb;
147     uint64_t state = 0;
148     int ret, data_type, data_size, offset;
149     enum AVCodecID codec;
150
151     while (!IS_LE_MARKER(state)) {
152         state = (state << 8) | avio_r8(pb);
153         if (avio_feof(pb))
154             return AVERROR_EOF;
155     }
156
157     if (IS_16LE_MARKER(state)) {
158         data_type = avio_rl16(pb);
159         data_size = avio_rl16(pb);
160     } else {
161         data_type = avio_rl24(pb);
162         data_size = avio_rl24(pb);
163     }
164
165     if ((ret = s337m_get_offset_and_codec(s, state, data_type, data_size, &offset, &codec)) < 0)
166         return ret;
167
168     if ((ret = av_get_packet(pb, pkt, offset)) != offset)
169         return ret < 0 ? ret : AVERROR_EOF;
170
171     if (IS_16LE_MARKER(state))
172         ff_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1);
173     else
174         bswap_buf24(pkt->data, pkt->size);
175
176     if (!s->nb_streams) {
177         AVStream *st = avformat_new_stream(s, NULL);
178         if (!st) {
179             return AVERROR(ENOMEM);
180         }
181         st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
182         st->codecpar->codec_id   = codec;
183         st->need_parsing         = AVSTREAM_PARSE_HEADERS;
184     }
185
186     return 0;
187 }
188
189 AVInputFormat ff_s337m_demuxer = {
190     .name           = "s337m",
191     .long_name      = NULL_IF_CONFIG_SMALL("SMPTE 337M"),
192     .read_probe     = s337m_probe,
193     .read_header    = s337m_read_header,
194     .read_packet    = s337m_read_packet,
195     .flags          = AVFMT_GENERIC_INDEX,
196 };