• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/parseutils.h"
24 #include "avio_internal.h"
25 #include "avformat.h"
26 #include "internal.h"
27 
28 typedef struct DHAVContext {
29     unsigned type;
30     unsigned subtype;
31     unsigned channel;
32     unsigned frame_subnumber;
33     unsigned frame_number;
34     unsigned date;
35     unsigned timestamp;
36     int width, height;
37     int video_codec;
38     int frame_rate;
39     int audio_channels;
40     int audio_codec;
41     int sample_rate;
42     int64_t last_good_pos;
43     int64_t duration;
44 
45     int video_stream_index;
46     int audio_stream_index;
47 } DHAVContext;
48 
49 typedef struct DHAVStream {
50     int64_t last_frame_number;
51     int64_t last_timestamp;
52     int64_t last_time;
53     int64_t pts;
54 } DHAVStream;
55 
dhav_probe(const AVProbeData * p)56 static int dhav_probe(const AVProbeData *p)
57 {
58     if (!memcmp(p->buf, "DAHUA", 5))
59         return AVPROBE_SCORE_MAX;
60 
61     if (memcmp(p->buf, "DHAV", 4))
62         return 0;
63 
64     if (p->buf[4] == 0xf0 ||
65         p->buf[4] == 0xf1 ||
66         p->buf[4] == 0xfc ||
67         p->buf[4] == 0xfd)
68         return AVPROBE_SCORE_MAX;
69     return 0;
70 }
71 
72 static const uint32_t sample_rates[] = {
73     8000, 4000, 8000, 11025, 16000,
74     20000, 22050, 32000, 44100, 48000,
75     96000, 192000, 64000,
76 };
77 
parse_ext(AVFormatContext * s,int length)78 static int parse_ext(AVFormatContext *s, int length)
79 {
80     DHAVContext *dhav = s->priv_data;
81     int64_t ret = 0;
82 
83     while (length > 0) {
84         int type = avio_r8(s->pb);
85         int index;
86 
87         switch (type) {
88         case 0x80:
89             ret = avio_skip(s->pb, 1);
90             dhav->width  = 8 * avio_r8(s->pb);
91             dhav->height = 8 * avio_r8(s->pb);
92             length -= 4;
93             break;
94         case 0x81:
95             ret = avio_skip(s->pb, 1);
96             dhav->video_codec = avio_r8(s->pb);
97             dhav->frame_rate = avio_r8(s->pb);
98             length -= 4;
99             break;
100         case 0x82:
101             ret = avio_skip(s->pb, 3);
102             dhav->width  = avio_rl16(s->pb);
103             dhav->height = avio_rl16(s->pb);
104             length -= 8;
105             break;
106         case 0x83:
107             dhav->audio_channels = avio_r8(s->pb);
108             dhav->audio_codec = avio_r8(s->pb);
109             index = avio_r8(s->pb);
110             if (index < FF_ARRAY_ELEMS(sample_rates)) {
111                 dhav->sample_rate = sample_rates[index];
112             } else {
113                 dhav->sample_rate = 8000;
114             }
115             length -= 4;
116             break;
117         case 0x88:
118             ret = avio_skip(s->pb, 7);
119             length -= 8;
120             break;
121         case 0x8c:
122             ret = avio_skip(s->pb, 1);
123             dhav->audio_channels = avio_r8(s->pb);
124             dhav->audio_codec = avio_r8(s->pb);
125             index = avio_r8(s->pb);
126             if (index < FF_ARRAY_ELEMS(sample_rates)) {
127                 dhav->sample_rate = sample_rates[index];
128             } else {
129                 dhav->sample_rate = 8000;
130             }
131             ret = avio_skip(s->pb, 3);
132             length -= 8;
133             break;
134         case 0x91:
135         case 0x92:
136         case 0x93:
137         case 0x95:
138         case 0x9a:
139         case 0x9b: // sample aspect ratio
140         case 0xb3:
141             ret = avio_skip(s->pb, 7);
142             length -= 8;
143             break;
144         case 0x84:
145         case 0x85:
146         case 0x8b:
147         case 0x94:
148         case 0x96:
149         case 0xa0:
150         case 0xb2:
151         case 0xb4:
152             ret = avio_skip(s->pb, 3);
153             length -= 4;
154             break;
155         default:
156             av_log(s, AV_LOG_INFO, "Unknown type: %X, skipping rest of header.\n", type);
157             ret = avio_skip(s->pb, length - 1);
158             length = 0;
159         }
160 
161         if (ret < 0)
162             return ret;
163     }
164 
165     return 0;
166 }
167 
read_chunk(AVFormatContext * s)168 static int read_chunk(AVFormatContext *s)
169 {
170     DHAVContext *dhav = s->priv_data;
171     int frame_length, ext_length;
172     int64_t start, end, ret;
173 
174     if (avio_feof(s->pb))
175         return AVERROR_EOF;
176 
177     while (avio_r8(s->pb) != 'D' || avio_r8(s->pb) != 'H' || avio_r8(s->pb) != 'A' || avio_r8(s->pb) != 'V') {
178         if (avio_feof(s->pb))
179             return AVERROR_EOF;
180     }
181 
182     start = avio_tell(s->pb) - 4;
183     dhav->last_good_pos = start;
184     dhav->type = avio_r8(s->pb);
185     dhav->subtype = avio_r8(s->pb);
186     dhav->channel = avio_r8(s->pb);
187     dhav->frame_subnumber = avio_r8(s->pb);
188     dhav->frame_number = avio_rl32(s->pb);
189     frame_length = avio_rl32(s->pb);
190     dhav->date = avio_rl32(s->pb);
191 
192     if (frame_length < 24)
193         return AVERROR_INVALIDDATA;
194     if (dhav->type == 0xf1) {
195         ret = avio_skip(s->pb, frame_length - 20);
196         return ret < 0 ? ret : 0;
197     }
198 
199     dhav->timestamp = avio_rl16(s->pb);
200     ext_length = avio_r8(s->pb);
201     avio_skip(s->pb, 1); // checksum
202 
203     ret = parse_ext(s, ext_length);
204     if (ret < 0)
205         return ret;
206 
207     end = avio_tell(s->pb);
208 
209     return frame_length - 8 - (end - start);
210 }
211 
get_timeinfo(unsigned date,struct tm * timeinfo)212 static void get_timeinfo(unsigned date, struct tm *timeinfo)
213 {
214     int year, month, day, hour, min, sec;
215 
216     sec   =   date        & 0x3F;
217     min   =  (date >>  6) & 0x3F;
218     hour  =  (date >> 12) & 0x1F;
219     day   =  (date >> 17) & 0x1F;
220     month =  (date >> 22) & 0x0F;
221     year  = ((date >> 26) & 0x3F) + 2000;
222 
223     timeinfo->tm_year = year - 1900;
224     timeinfo->tm_mon  = month - 1;
225     timeinfo->tm_mday = day;
226     timeinfo->tm_hour = hour;
227     timeinfo->tm_min  = min;
228     timeinfo->tm_sec  = sec;
229 }
230 
get_duration(AVFormatContext * s)231 static int64_t get_duration(AVFormatContext *s)
232 {
233     DHAVContext *dhav = s->priv_data;
234     int64_t start_pos = avio_tell(s->pb);
235     int64_t start = 0, end = 0;
236     struct tm timeinfo;
237     int max_interations = 100000;
238 
239     if (!s->pb->seekable)
240         return 0;
241 
242     avio_seek(s->pb, avio_size(s->pb) - 8, SEEK_SET);
243     while (avio_tell(s->pb) > 12 && max_interations--) {
244         if (avio_rl32(s->pb) == MKTAG('d','h','a','v')) {
245             int64_t seek_back = avio_rl32(s->pb);
246 
247             avio_seek(s->pb, -seek_back, SEEK_CUR);
248             read_chunk(s);
249             get_timeinfo(dhav->date, &timeinfo);
250             end = av_timegm(&timeinfo) * 1000LL;
251             break;
252         } else {
253             avio_seek(s->pb, -12, SEEK_CUR);
254         }
255     }
256 
257     avio_seek(s->pb, start_pos, SEEK_SET);
258 
259     read_chunk(s);
260     get_timeinfo(dhav->date, &timeinfo);
261     start = av_timegm(&timeinfo) * 1000LL;
262 
263     avio_seek(s->pb, start_pos, SEEK_SET);
264 
265     return end - start;
266 }
267 
dhav_read_header(AVFormatContext * s)268 static int dhav_read_header(AVFormatContext *s)
269 {
270     DHAVContext *dhav = s->priv_data;
271     uint8_t signature[5];
272 
273     ffio_ensure_seekback(s->pb, 5);
274     avio_read(s->pb, signature, sizeof(signature));
275     if (!memcmp(signature, "DAHUA", 5)) {
276         avio_skip(s->pb, 0x400 - 5);
277         dhav->last_good_pos = avio_tell(s->pb);
278     } else {
279         if (!memcmp(signature, "DHAV", 4)) {
280             avio_seek(s->pb, -5, SEEK_CUR);
281             dhav->last_good_pos = avio_tell(s->pb);
282         } else if (s->pb->seekable) {
283             avio_seek(s->pb, avio_size(s->pb) - 8, SEEK_SET);
284             while (avio_rl32(s->pb) == MKTAG('d','h','a','v')) {
285                 int seek_back;
286 
287                 seek_back = avio_rl32(s->pb) + 8;
288                 if (seek_back < 9)
289                     break;
290                 dhav->last_good_pos = avio_tell(s->pb);
291                 avio_seek(s->pb, -seek_back, SEEK_CUR);
292             }
293             avio_seek(s->pb, dhav->last_good_pos, SEEK_SET);
294         }
295     }
296 
297     dhav->duration = get_duration(s);
298     dhav->last_good_pos = avio_tell(s->pb);
299     s->ctx_flags |= AVFMTCTX_NOHEADER;
300     dhav->video_stream_index = -1;
301     dhav->audio_stream_index = -1;
302 
303     return 0;
304 }
305 
get_pts(AVFormatContext * s,int stream_index)306 static int64_t get_pts(AVFormatContext *s, int stream_index)
307 {
308     DHAVStream *dst = s->streams[stream_index]->priv_data;
309     DHAVContext *dhav = s->priv_data;
310     struct tm timeinfo;
311     time_t t;
312 
313     get_timeinfo(dhav->date, &timeinfo);
314 
315     t = av_timegm(&timeinfo);
316     if (dst->last_time == t) {
317         int64_t diff = dhav->timestamp - dst->last_timestamp;
318 
319         if (diff < 0)
320             diff += 65535;
321         if (diff == 0 && dhav->frame_rate)
322             diff = av_rescale(dhav->frame_number - dst->last_frame_number, 1000, dhav->frame_rate);
323         dst->pts += diff;
324     } else {
325         dst->pts = t * 1000LL;
326     }
327 
328     dst->last_time = t;
329     dst->last_timestamp = dhav->timestamp;
330     dst->last_frame_number = dhav->frame_number;
331 
332     return dst->pts;
333 }
334 
dhav_read_packet(AVFormatContext * s,AVPacket * pkt)335 static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt)
336 {
337     DHAVContext *dhav = s->priv_data;
338     int size, ret, stream_index;
339 
340 retry:
341     while ((ret = read_chunk(s)) == 0)
342         ;
343 
344     if (ret < 0)
345         return ret;
346 
347     if (dhav->type == 0xfd && dhav->video_stream_index == -1) {
348         AVStream *st = avformat_new_stream(s, NULL);
349         DHAVStream *dst;
350 
351         if (!st)
352             return AVERROR(ENOMEM);
353 
354         st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
355         switch (dhav->video_codec) {
356         case 0x1: st->codecpar->codec_id = AV_CODEC_ID_MPEG4; break;
357         case 0x3: st->codecpar->codec_id = AV_CODEC_ID_MJPEG; break;
358         case 0x2:
359         case 0x4:
360         case 0x8: st->codecpar->codec_id = AV_CODEC_ID_H264;  break;
361         case 0xc: st->codecpar->codec_id = AV_CODEC_ID_HEVC;  break;
362         default: avpriv_request_sample(s, "Unknown video codec %X", dhav->video_codec);
363         }
364         st->duration             = dhav->duration;
365         st->codecpar->width      = dhav->width;
366         st->codecpar->height     = dhav->height;
367         st->avg_frame_rate.num   = dhav->frame_rate;
368         st->avg_frame_rate.den   = 1;
369         st->priv_data = dst = av_mallocz(sizeof(DHAVStream));
370         if (!st->priv_data)
371             return AVERROR(ENOMEM);
372         dst->last_time = AV_NOPTS_VALUE;
373         dhav->video_stream_index = st->index;
374 
375         avpriv_set_pts_info(st, 64, 1, 1000);
376     } else if (dhav->type == 0xf0 && dhav->audio_stream_index == -1) {
377         AVStream *st = avformat_new_stream(s, NULL);
378         DHAVStream *dst;
379 
380         if (!st)
381             return AVERROR(ENOMEM);
382 
383         st->codecpar->codec_type  = AVMEDIA_TYPE_AUDIO;
384         switch (dhav->audio_codec) {
385         case 0x07: st->codecpar->codec_id = AV_CODEC_ID_PCM_S8;    break;
386         case 0x0c: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; break;
387         case 0x10: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; break;
388         case 0x0a: st->codecpar->codec_id = AV_CODEC_ID_PCM_MULAW; break;
389         case 0x16: st->codecpar->codec_id = AV_CODEC_ID_PCM_MULAW; break;
390         case 0x0e: st->codecpar->codec_id = AV_CODEC_ID_PCM_ALAW;  break;
391         case 0x1a: st->codecpar->codec_id = AV_CODEC_ID_AAC;       break;
392         case 0x1f: st->codecpar->codec_id = AV_CODEC_ID_MP2;       break;
393         case 0x21: st->codecpar->codec_id = AV_CODEC_ID_MP3;       break;
394         case 0x0d: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_MS;  break;
395         default: avpriv_request_sample(s, "Unknown audio codec %X", dhav->audio_codec);
396         }
397         st->duration              = dhav->duration;
398         st->codecpar->ch_layout.nb_channels = dhav->audio_channels;
399         st->codecpar->sample_rate = dhav->sample_rate;
400         st->priv_data = dst = av_mallocz(sizeof(DHAVStream));
401         if (!st->priv_data)
402             return AVERROR(ENOMEM);
403         dst->last_time = AV_NOPTS_VALUE;
404         dhav->audio_stream_index  = st->index;
405 
406         avpriv_set_pts_info(st, 64, 1, 1000);
407     }
408 
409     stream_index = dhav->type == 0xf0 ? dhav->audio_stream_index : dhav->video_stream_index;
410     if (stream_index < 0) {
411         avio_skip(s->pb, ret);
412         if (avio_rl32(s->pb) == MKTAG('d','h','a','v'))
413             avio_skip(s->pb, 4);
414         goto retry;
415     }
416 
417     size = ret;
418     ret = av_get_packet(s->pb, pkt, size);
419     if (ret < 0)
420         return ret;
421     pkt->stream_index = stream_index;
422     if (dhav->type != 0xfc)
423         pkt->flags   |= AV_PKT_FLAG_KEY;
424     pkt->duration = 1;
425     if (pkt->stream_index >= 0)
426         pkt->pts = get_pts(s, pkt->stream_index);
427     pkt->pos = dhav->last_good_pos;
428     if (avio_rl32(s->pb) == MKTAG('d','h','a','v'))
429         avio_skip(s->pb, 4);
430 
431     return ret;
432 }
433 
dhav_read_seek(AVFormatContext * s,int stream_index,int64_t timestamp,int flags)434 static int dhav_read_seek(AVFormatContext *s, int stream_index,
435                           int64_t timestamp, int flags)
436 {
437     DHAVContext *dhav = s->priv_data;
438     AVStream *st = s->streams[stream_index];
439     FFStream *const sti = ffstream(st);
440     int index = av_index_search_timestamp(st, timestamp, flags);
441     int64_t pts;
442 
443     if (index < 0)
444         return -1;
445     pts = sti->index_entries[index].timestamp;
446     if (pts < timestamp)
447         return AVERROR(EAGAIN);
448     if (avio_seek(s->pb, sti->index_entries[index].pos, SEEK_SET) < 0)
449         return -1;
450 
451     for (int n = 0; n < s->nb_streams; n++) {
452         AVStream *st = s->streams[n];
453         DHAVStream *dst = st->priv_data;
454 
455         dst->pts = pts;
456         dst->last_time = AV_NOPTS_VALUE;
457     }
458     dhav->last_good_pos = avio_tell(s->pb);
459 
460     return 0;
461 }
462 
463 const AVInputFormat ff_dhav_demuxer = {
464     .name           = "dhav",
465     .long_name      = NULL_IF_CONFIG_SMALL("Video DAV"),
466     .priv_data_size = sizeof(DHAVContext),
467     .read_probe     = dhav_probe,
468     .read_header    = dhav_read_header,
469     .read_packet    = dhav_read_packet,
470     .read_seek      = dhav_read_seek,
471     .extensions     = "dav",
472     .flags          = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_TS_DISCONT | AVFMT_TS_NONSTRICT | AVFMT_SEEK_TO_PTS,
473 };
474