• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
s337m_get_offset_and_codec(void * avc,uint64_t state,int data_type,int data_size,int * offset,enum AVCodecID * codec)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 
s337m_probe(const AVProbeData * p)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 
s337m_read_header(AVFormatContext * s)130 static int s337m_read_header(AVFormatContext *s)
131 {
132     s->ctx_flags |= AVFMTCTX_NOHEADER;
133     return 0;
134 }
135 
bswap_buf24(uint8_t * data,int size)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 
s337m_read_packet(AVFormatContext * s,AVPacket * pkt)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     int64_t pos;
151 
152     while (!IS_LE_MARKER(state)) {
153         state = (state << 8) | avio_r8(pb);
154         if (avio_feof(pb))
155             return AVERROR_EOF;
156     }
157 
158     if (IS_16LE_MARKER(state)) {
159         data_type = avio_rl16(pb);
160         data_size = avio_rl16(pb);
161     } else {
162         data_type = avio_rl24(pb);
163         data_size = avio_rl24(pb);
164     }
165 
166     pos = avio_tell(pb);
167 
168     if ((ret = s337m_get_offset_and_codec(s, state, data_type, data_size, &offset, &codec)) < 0)
169         return ret;
170 
171     if ((ret = av_new_packet(pkt, offset)) < 0)
172         return ret;
173 
174     pkt->pos = pos;
175 
176     if (avio_read(pb, pkt->data, pkt->size) < pkt->size) {
177         return AVERROR_EOF;
178     }
179 
180     if (IS_16LE_MARKER(state))
181         ff_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1);
182     else
183         bswap_buf24(pkt->data, pkt->size);
184 
185     if (!s->nb_streams) {
186         AVStream *st = avformat_new_stream(s, NULL);
187         if (!st) {
188             return AVERROR(ENOMEM);
189         }
190         st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
191         st->codecpar->codec_id   = codec;
192     }
193 
194     return 0;
195 }
196 
197 AVInputFormat ff_s337m_demuxer = {
198     .name           = "s337m",
199     .long_name      = NULL_IF_CONFIG_SMALL("SMPTE 337M"),
200     .read_probe     = s337m_probe,
201     .read_header    = s337m_read_header,
202     .read_packet    = s337m_read_packet,
203     .flags          = AVFMT_GENERIC_INDEX,
204 };
205