]> git.sesse.net Git - ffmpeg/blob - libavformat/dhav.c
avformat: add DHAV demuxer
[ffmpeg] / libavformat / dhav.c
1 /*
2  * DHAV demuxer
3  *
4  * Copyright (c) 2018 Paul B Mahol
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with FFmpeg; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22
23 #include "libavutil/avassert.h"
24 #include "avio_internal.h"
25 #include "avformat.h"
26 #include "internal.h"
27
28 typedef struct DHAVContext {
29     unsigned type;
30     int width, height;
31     int video_codec;
32     int frame_rate;
33     int channels;
34     int audio_codec;
35     int sample_rate;
36     int64_t pts;
37
38     int video_stream_index;
39     int audio_stream_index;
40 } DHAVContext;
41
42 static int dhav_probe(AVProbeData *p)
43 {
44     if (!memcmp(p->buf, "DAHUA", 5))
45         return AVPROBE_SCORE_MAX;
46
47     if (memcmp(p->buf, "DHAV", 4))
48         return 0;
49
50     if (p->buf[4] == 0xf0 ||
51         p->buf[4] == 0xf1 ||
52         p->buf[4] == 0xfc ||
53         p->buf[4] == 0xfd)
54         return AVPROBE_SCORE_MAX;
55     return 0;
56 }
57
58 static int dhav_read_header(AVFormatContext *s)
59 {
60     DHAVContext *dhav = s->priv_data;
61     uint8_t signature[5];
62
63     ffio_ensure_seekback(s->pb, 5);
64     avio_read(s->pb, signature, sizeof(signature));
65     if (!memcmp(signature, "DAHUA", 5))
66         avio_skip(s->pb, 0x400 - 5);
67     else
68         avio_seek(s->pb, -5, SEEK_CUR);
69
70     s->ctx_flags |= AVFMTCTX_NOHEADER;
71     dhav->video_stream_index = -1;
72     dhav->audio_stream_index = -1;
73
74     return 0;
75 }
76
77 static const uint32_t sample_rates[] = {
78     8000, 4000, 8000, 11025, 16000,
79     20000, 22050, 32000, 44100, 48000,
80     96000, 192000, 64000,
81 };
82
83 static int parse_ext(AVFormatContext *s, int length)
84 {
85     DHAVContext *dhav = s->priv_data;
86     int index;
87
88     while (length > 0) {
89         int type = avio_r8(s->pb);
90
91         switch (type) {
92         case 0x80:
93             avio_skip(s->pb, 1);
94             dhav->width  = 8 * avio_r8(s->pb);
95             dhav->height = 8 * avio_r8(s->pb);
96             length -= 4;
97             break;
98         case 0x81:
99             avio_skip(s->pb, 1);
100             dhav->video_codec = avio_r8(s->pb);
101             dhav->frame_rate = avio_r8(s->pb);
102             length -= 4;
103             break;
104         case 0x82:
105             avio_skip(s->pb, 3);
106             dhav->width  = avio_rl16(s->pb);
107             dhav->height = avio_rl16(s->pb);
108             length -= 8;
109             break;
110         case 0x83:
111             dhav->channels = avio_r8(s->pb);
112             dhav->audio_codec = avio_r8(s->pb);
113             index = avio_r8(s->pb);
114             if (index < FF_ARRAY_ELEMS(sample_rates)) {
115                 dhav->sample_rate = sample_rates[index];
116             } else {
117                 dhav->sample_rate = 8000;
118             }
119             length -= 4;
120             break;
121         case 0x88:
122             avio_skip(s->pb, 7);
123             length -= 8;
124             break;
125         case 0x8c:
126             avio_skip(s->pb, 1);
127             dhav->channels = avio_r8(s->pb);
128             dhav->audio_codec = avio_r8(s->pb);
129             index = avio_r8(s->pb);
130             if (index < FF_ARRAY_ELEMS(sample_rates)) {
131                 dhav->sample_rate = sample_rates[index];
132             } else {
133                 dhav->sample_rate = 8000;
134             }
135             avio_skip(s->pb, 3);
136             length -= 8;
137             break;
138         case 0x91:
139         case 0x92:
140         case 0x93:
141         case 0x95:
142         case 0x9a:
143         case 0x9b: // sample aspect ratio
144         case 0xb3:
145             avio_skip(s->pb, 7);
146             length -= 8;
147             break;
148         case 0x84:
149         case 0x85:
150         case 0x8b:
151         case 0x94:
152         case 0x96:
153         case 0xa0:
154         case 0xb2:
155         case 0xb4:
156             avio_skip(s->pb, 3);
157             length -= 4;
158             break;
159         default:
160             av_log(s, AV_LOG_INFO, "Unknown type: %X, skipping rest of header.\n", type);
161             avio_skip(s->pb, length - 1);
162             length = 0;
163         }
164     }
165
166     return 0;
167 }
168
169 static int read_chunk(AVFormatContext *s)
170 {
171     DHAVContext *dhav = s->priv_data;
172     unsigned size, skip;
173     int64_t start, end;
174     int ret;
175
176     start = avio_tell(s->pb);
177
178     if (avio_feof(s->pb))
179         return AVERROR_EOF;
180
181     if (avio_rl32(s->pb) != MKTAG('D','H','A','V'))
182         return AVERROR_INVALIDDATA;
183
184     dhav->type = avio_r8(s->pb);
185     avio_skip(s->pb, 3);
186     dhav->pts = avio_rl32(s->pb);
187     size = avio_rl32(s->pb);
188     if (size < 24)
189         return AVERROR_INVALIDDATA;
190     if (dhav->type == 0xf1) {
191         avio_skip(s->pb, size - 16);
192         return 0;
193     }
194
195     avio_rl32(s->pb);
196     avio_skip(s->pb, 2);
197     skip = avio_r8(s->pb);
198     avio_skip(s->pb, 1);
199
200     ret = parse_ext(s, skip);
201     if (ret < 0)
202         return ret;
203
204     end = avio_tell(s->pb);
205
206     return size - 8 - (end - start);
207 }
208
209 static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt)
210 {
211     DHAVContext *dhav = s->priv_data;
212     int64_t start;
213     int ret;
214
215     start = avio_tell(s->pb);
216
217     while ((ret = read_chunk(s)) == 0)
218         ;
219
220     if (ret < 0)
221         return ret;
222
223     if (dhav->type == 0xfd && dhav->video_stream_index == -1) {
224         AVStream *st = avformat_new_stream(s, NULL);
225
226         if (!st)
227             return AVERROR(ENOMEM);
228
229         st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
230         switch (dhav->video_codec) {
231         case 0x1: st->codecpar->codec_id = AV_CODEC_ID_MPEG4; break;
232         case 0x3: st->codecpar->codec_id = AV_CODEC_ID_MJPEG; break;
233         case 0x2:
234         case 0x4:
235         case 0x8: st->codecpar->codec_id = AV_CODEC_ID_H264;  break;
236         case 0xc: st->codecpar->codec_id = AV_CODEC_ID_HEVC;  break;
237         default: avpriv_request_sample(s, "Unknown video codec %X\n", dhav->video_codec);
238         }
239         st->codecpar->width      = dhav->width;
240         st->codecpar->height     = dhav->height;
241         dhav->video_stream_index = st->index;
242
243         avpriv_set_pts_info(st, 64, 1, dhav->frame_rate);
244     } else if (dhav->type == 0xf0 && dhav->audio_stream_index == -1) {
245         AVStream *st = avformat_new_stream(s, NULL);
246
247         if (!st)
248             return AVERROR(ENOMEM);
249
250         st->codecpar->codec_type  = AVMEDIA_TYPE_AUDIO;
251         switch (dhav->audio_codec) {
252         case 0x07: st->codecpar->codec_id = AV_CODEC_ID_PCM_S8;    break;
253         case 0x0c: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; break;
254         case 0x10: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; break;
255         case 0x0a: st->codecpar->codec_id = AV_CODEC_ID_PCM_MULAW; break;
256         case 0x16: st->codecpar->codec_id = AV_CODEC_ID_PCM_MULAW; break;
257         case 0x0e: st->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;  break;
258         case 0x1a: st->codecpar->codec_id = AV_CODEC_ID_AAC;       break;
259         case 0x1f: st->codecpar->codec_id = AV_CODEC_ID_MP2;       break;
260         case 0x21: st->codecpar->codec_id = AV_CODEC_ID_MP3;       break;
261         case 0x0d: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_MS;  break;
262         default: avpriv_request_sample(s, "Unknown audio codec %X\n", dhav->audio_codec);
263         }
264         st->codecpar->channels    = dhav->channels;
265         st->codecpar->sample_rate = dhav->sample_rate;
266         dhav->audio_stream_index  = st->index;
267
268         avpriv_set_pts_info(st, 64, ret, dhav->sample_rate);
269     }
270
271     ret = av_get_packet(s->pb, pkt, ret);
272     if (ret < 0)
273         return ret;
274     pkt->stream_index = dhav->type == 0xf0 ? dhav->audio_stream_index : dhav->video_stream_index;
275     if (dhav->type != 0xfc)
276         pkt->flags   |= AV_PKT_FLAG_KEY;
277     pkt->pts = dhav->pts;
278     pkt->duration = 1;
279     pkt->pos = start;
280     if (avio_rl32(s->pb) != MKTAG('d','h','a','v'))
281         return AVERROR_INVALIDDATA;
282     avio_skip(s->pb, 4);
283
284     return ret;
285 }
286
287 AVInputFormat ff_dhav_demuxer = {
288     .name           = "dhav",
289     .long_name      = NULL_IF_CONFIG_SMALL("Video DAV"),
290     .priv_data_size = sizeof(DHAVContext),
291     .read_probe     = dhav_probe,
292     .read_header    = dhav_read_header,
293     .read_packet    = dhav_read_packet,
294     .extensions     = "dav",
295     .flags          = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK,
296 };