yading@11
|
1 /*
|
yading@11
|
2 * NuppelVideo demuxer.
|
yading@11
|
3 * Copyright (c) 2006 Reimar Doeffinger
|
yading@11
|
4 *
|
yading@11
|
5 * This file is part of FFmpeg.
|
yading@11
|
6 *
|
yading@11
|
7 * FFmpeg is free software; you can redistribute it and/or
|
yading@11
|
8 * modify it under the terms of the GNU Lesser General Public
|
yading@11
|
9 * License as published by the Free Software Foundation; either
|
yading@11
|
10 * version 2.1 of the License, or (at your option) any later version.
|
yading@11
|
11 *
|
yading@11
|
12 * FFmpeg is distributed in the hope that it will be useful,
|
yading@11
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@11
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@11
|
15 * Lesser General Public License for more details.
|
yading@11
|
16 *
|
yading@11
|
17 * You should have received a copy of the GNU Lesser General Public
|
yading@11
|
18 * License along with FFmpeg; if not, write to the Free Software
|
yading@11
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@11
|
20 */
|
yading@11
|
21
|
yading@11
|
22 #include "libavutil/channel_layout.h"
|
yading@11
|
23 #include "libavutil/intreadwrite.h"
|
yading@11
|
24 #include "libavutil/intfloat.h"
|
yading@11
|
25 #include "avformat.h"
|
yading@11
|
26 #include "internal.h"
|
yading@11
|
27 #include "riff.h"
|
yading@11
|
28
|
yading@11
|
29 static const AVCodecTag nuv_audio_tags[] = {
|
yading@11
|
30 { AV_CODEC_ID_PCM_S16LE, MKTAG('R', 'A', 'W', 'A') },
|
yading@11
|
31 { AV_CODEC_ID_MP3, MKTAG('L', 'A', 'M', 'E') },
|
yading@11
|
32 { AV_CODEC_ID_NONE, 0 },
|
yading@11
|
33 };
|
yading@11
|
34
|
yading@11
|
35 typedef struct {
|
yading@11
|
36 int v_id;
|
yading@11
|
37 int a_id;
|
yading@11
|
38 int rtjpg_video;
|
yading@11
|
39 } NUVContext;
|
yading@11
|
40
|
yading@11
|
41 typedef enum {
|
yading@11
|
42 NUV_VIDEO = 'V',
|
yading@11
|
43 NUV_EXTRADATA = 'D',
|
yading@11
|
44 NUV_AUDIO = 'A',
|
yading@11
|
45 NUV_SEEKP = 'R',
|
yading@11
|
46 NUV_MYTHEXT = 'X'
|
yading@11
|
47 } nuv_frametype;
|
yading@11
|
48
|
yading@11
|
49 static int nuv_probe(AVProbeData *p)
|
yading@11
|
50 {
|
yading@11
|
51 if (!memcmp(p->buf, "NuppelVideo", 12))
|
yading@11
|
52 return AVPROBE_SCORE_MAX;
|
yading@11
|
53 if (!memcmp(p->buf, "MythTVVideo", 12))
|
yading@11
|
54 return AVPROBE_SCORE_MAX;
|
yading@11
|
55 return 0;
|
yading@11
|
56 }
|
yading@11
|
57
|
yading@11
|
58 /// little macro to sanitize packet size
|
yading@11
|
59 #define PKTSIZE(s) (s & 0xffffff)
|
yading@11
|
60
|
yading@11
|
61 /**
|
yading@11
|
62 * @brief read until we found all data needed for decoding
|
yading@11
|
63 * @param vst video stream of which to change parameters
|
yading@11
|
64 * @param ast video stream of which to change parameters
|
yading@11
|
65 * @param myth set if this is a MythTVVideo format file
|
yading@11
|
66 * @return 0 or AVERROR code
|
yading@11
|
67 */
|
yading@11
|
68 static int get_codec_data(AVIOContext *pb, AVStream *vst,
|
yading@11
|
69 AVStream *ast, int myth)
|
yading@11
|
70 {
|
yading@11
|
71 nuv_frametype frametype;
|
yading@11
|
72
|
yading@11
|
73 if (!vst && !myth)
|
yading@11
|
74 return 1; // no codec data needed
|
yading@11
|
75 while (!url_feof(pb)) {
|
yading@11
|
76 int size, subtype;
|
yading@11
|
77
|
yading@11
|
78 frametype = avio_r8(pb);
|
yading@11
|
79 switch (frametype) {
|
yading@11
|
80 case NUV_EXTRADATA:
|
yading@11
|
81 subtype = avio_r8(pb);
|
yading@11
|
82 avio_skip(pb, 6);
|
yading@11
|
83 size = PKTSIZE(avio_rl32(pb));
|
yading@11
|
84 if (vst && subtype == 'R') {
|
yading@11
|
85 if (vst->codec->extradata) {
|
yading@11
|
86 av_freep(&vst->codec->extradata);
|
yading@11
|
87 vst->codec->extradata_size = 0;
|
yading@11
|
88 }
|
yading@11
|
89 vst->codec->extradata = av_malloc(size);
|
yading@11
|
90 if (!vst->codec->extradata)
|
yading@11
|
91 return AVERROR(ENOMEM);
|
yading@11
|
92 vst->codec->extradata_size = size;
|
yading@11
|
93 avio_read(pb, vst->codec->extradata, size);
|
yading@11
|
94 size = 0;
|
yading@11
|
95 if (!myth)
|
yading@11
|
96 return 0;
|
yading@11
|
97 }
|
yading@11
|
98 break;
|
yading@11
|
99 case NUV_MYTHEXT:
|
yading@11
|
100 avio_skip(pb, 7);
|
yading@11
|
101 size = PKTSIZE(avio_rl32(pb));
|
yading@11
|
102 if (size != 128 * 4)
|
yading@11
|
103 break;
|
yading@11
|
104 avio_rl32(pb); // version
|
yading@11
|
105 if (vst) {
|
yading@11
|
106 vst->codec->codec_tag = avio_rl32(pb);
|
yading@11
|
107 vst->codec->codec_id =
|
yading@11
|
108 ff_codec_get_id(ff_codec_bmp_tags, vst->codec->codec_tag);
|
yading@11
|
109 if (vst->codec->codec_tag == MKTAG('R', 'J', 'P', 'G'))
|
yading@11
|
110 vst->codec->codec_id = AV_CODEC_ID_NUV;
|
yading@11
|
111 } else
|
yading@11
|
112 avio_skip(pb, 4);
|
yading@11
|
113
|
yading@11
|
114 if (ast) {
|
yading@11
|
115 int id;
|
yading@11
|
116
|
yading@11
|
117 ast->codec->codec_tag = avio_rl32(pb);
|
yading@11
|
118 ast->codec->sample_rate = avio_rl32(pb);
|
yading@11
|
119 ast->codec->bits_per_coded_sample = avio_rl32(pb);
|
yading@11
|
120 ast->codec->channels = avio_rl32(pb);
|
yading@11
|
121 ast->codec->channel_layout = 0;
|
yading@11
|
122
|
yading@11
|
123 id = ff_wav_codec_get_id(ast->codec->codec_tag,
|
yading@11
|
124 ast->codec->bits_per_coded_sample);
|
yading@11
|
125 if (id == AV_CODEC_ID_NONE) {
|
yading@11
|
126 id = ff_codec_get_id(nuv_audio_tags, ast->codec->codec_tag);
|
yading@11
|
127 if (id == AV_CODEC_ID_PCM_S16LE)
|
yading@11
|
128 id = ff_get_pcm_codec_id(ast->codec->bits_per_coded_sample,
|
yading@11
|
129 0, 0, ~1);
|
yading@11
|
130 }
|
yading@11
|
131 ast->codec->codec_id = id;
|
yading@11
|
132
|
yading@11
|
133 ast->need_parsing = AVSTREAM_PARSE_FULL;
|
yading@11
|
134 } else
|
yading@11
|
135 avio_skip(pb, 4 * 4);
|
yading@11
|
136
|
yading@11
|
137 size -= 6 * 4;
|
yading@11
|
138 avio_skip(pb, size);
|
yading@11
|
139 return 0;
|
yading@11
|
140 case NUV_SEEKP:
|
yading@11
|
141 size = 11;
|
yading@11
|
142 break;
|
yading@11
|
143 default:
|
yading@11
|
144 avio_skip(pb, 7);
|
yading@11
|
145 size = PKTSIZE(avio_rl32(pb));
|
yading@11
|
146 break;
|
yading@11
|
147 }
|
yading@11
|
148 avio_skip(pb, size);
|
yading@11
|
149 }
|
yading@11
|
150
|
yading@11
|
151 return 0;
|
yading@11
|
152 }
|
yading@11
|
153
|
yading@11
|
154 static int nuv_header(AVFormatContext *s)
|
yading@11
|
155 {
|
yading@11
|
156 NUVContext *ctx = s->priv_data;
|
yading@11
|
157 AVIOContext *pb = s->pb;
|
yading@11
|
158 char id_string[12];
|
yading@11
|
159 double aspect, fps;
|
yading@11
|
160 int is_mythtv, width, height, v_packs, a_packs, ret;
|
yading@11
|
161 AVStream *vst = NULL, *ast = NULL;
|
yading@11
|
162
|
yading@11
|
163 avio_read(pb, id_string, 12);
|
yading@11
|
164 is_mythtv = !memcmp(id_string, "MythTVVideo", 12);
|
yading@11
|
165 avio_skip(pb, 5); // version string
|
yading@11
|
166 avio_skip(pb, 3); // padding
|
yading@11
|
167 width = avio_rl32(pb);
|
yading@11
|
168 height = avio_rl32(pb);
|
yading@11
|
169 avio_rl32(pb); // unused, "desiredwidth"
|
yading@11
|
170 avio_rl32(pb); // unused, "desiredheight"
|
yading@11
|
171 avio_r8(pb); // 'P' == progressive, 'I' == interlaced
|
yading@11
|
172 avio_skip(pb, 3); // padding
|
yading@11
|
173 aspect = av_int2double(avio_rl64(pb));
|
yading@11
|
174 if (aspect > 0.9999 && aspect < 1.0001)
|
yading@11
|
175 aspect = 4.0 / 3.0;
|
yading@11
|
176 fps = av_int2double(avio_rl64(pb));
|
yading@11
|
177
|
yading@11
|
178 // number of packets per stream type, -1 means unknown, e.g. streaming
|
yading@11
|
179 v_packs = avio_rl32(pb);
|
yading@11
|
180 a_packs = avio_rl32(pb);
|
yading@11
|
181 avio_rl32(pb); // text
|
yading@11
|
182
|
yading@11
|
183 avio_rl32(pb); // keyframe distance (?)
|
yading@11
|
184
|
yading@11
|
185 if (v_packs) {
|
yading@11
|
186 vst = avformat_new_stream(s, NULL);
|
yading@11
|
187 if (!vst)
|
yading@11
|
188 return AVERROR(ENOMEM);
|
yading@11
|
189 ctx->v_id = vst->index;
|
yading@11
|
190
|
yading@11
|
191 vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
yading@11
|
192 vst->codec->codec_id = AV_CODEC_ID_NUV;
|
yading@11
|
193 vst->codec->width = width;
|
yading@11
|
194 vst->codec->height = height;
|
yading@11
|
195 vst->codec->bits_per_coded_sample = 10;
|
yading@11
|
196 vst->sample_aspect_ratio = av_d2q(aspect * height / width,
|
yading@11
|
197 10000);
|
yading@11
|
198 #if FF_API_R_FRAME_RATE
|
yading@11
|
199 vst->r_frame_rate =
|
yading@11
|
200 #endif
|
yading@11
|
201 vst->avg_frame_rate = av_d2q(fps, 60000);
|
yading@11
|
202 avpriv_set_pts_info(vst, 32, 1, 1000);
|
yading@11
|
203 } else
|
yading@11
|
204 ctx->v_id = -1;
|
yading@11
|
205
|
yading@11
|
206 if (a_packs) {
|
yading@11
|
207 ast = avformat_new_stream(s, NULL);
|
yading@11
|
208 if (!ast)
|
yading@11
|
209 return AVERROR(ENOMEM);
|
yading@11
|
210 ctx->a_id = ast->index;
|
yading@11
|
211
|
yading@11
|
212 ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
yading@11
|
213 ast->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
|
yading@11
|
214 ast->codec->channels = 2;
|
yading@11
|
215 ast->codec->channel_layout = AV_CH_LAYOUT_STEREO;
|
yading@11
|
216 ast->codec->sample_rate = 44100;
|
yading@11
|
217 ast->codec->bit_rate = 2 * 2 * 44100 * 8;
|
yading@11
|
218 ast->codec->block_align = 2 * 2;
|
yading@11
|
219 ast->codec->bits_per_coded_sample = 16;
|
yading@11
|
220 avpriv_set_pts_info(ast, 32, 1, 1000);
|
yading@11
|
221 } else
|
yading@11
|
222 ctx->a_id = -1;
|
yading@11
|
223
|
yading@11
|
224 if ((ret = get_codec_data(pb, vst, ast, is_mythtv)) < 0)
|
yading@11
|
225 return ret;
|
yading@11
|
226
|
yading@11
|
227 ctx->rtjpg_video = vst && vst->codec->codec_id == AV_CODEC_ID_NUV;
|
yading@11
|
228
|
yading@11
|
229 return 0;
|
yading@11
|
230 }
|
yading@11
|
231
|
yading@11
|
232 #define HDRSIZE 12
|
yading@11
|
233
|
yading@11
|
234 static int nuv_packet(AVFormatContext *s, AVPacket *pkt)
|
yading@11
|
235 {
|
yading@11
|
236 NUVContext *ctx = s->priv_data;
|
yading@11
|
237 AVIOContext *pb = s->pb;
|
yading@11
|
238 uint8_t hdr[HDRSIZE];
|
yading@11
|
239 nuv_frametype frametype;
|
yading@11
|
240 int ret, size;
|
yading@11
|
241
|
yading@11
|
242 while (!url_feof(pb)) {
|
yading@11
|
243 int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0;
|
yading@11
|
244 uint64_t pos = avio_tell(pb);
|
yading@11
|
245
|
yading@11
|
246 ret = avio_read(pb, hdr, HDRSIZE);
|
yading@11
|
247 if (ret < HDRSIZE)
|
yading@11
|
248 return ret < 0 ? ret : AVERROR(EIO);
|
yading@11
|
249
|
yading@11
|
250 frametype = hdr[0];
|
yading@11
|
251 size = PKTSIZE(AV_RL32(&hdr[8]));
|
yading@11
|
252
|
yading@11
|
253 switch (frametype) {
|
yading@11
|
254 case NUV_EXTRADATA:
|
yading@11
|
255 if (!ctx->rtjpg_video) {
|
yading@11
|
256 avio_skip(pb, size);
|
yading@11
|
257 break;
|
yading@11
|
258 }
|
yading@11
|
259 case NUV_VIDEO:
|
yading@11
|
260 if (ctx->v_id < 0) {
|
yading@11
|
261 av_log(s, AV_LOG_ERROR, "Video packet in file without video stream!\n");
|
yading@11
|
262 avio_skip(pb, size);
|
yading@11
|
263 break;
|
yading@11
|
264 }
|
yading@11
|
265 ret = av_new_packet(pkt, copyhdrsize + size);
|
yading@11
|
266 if (ret < 0)
|
yading@11
|
267 return ret;
|
yading@11
|
268
|
yading@11
|
269 pkt->pos = pos;
|
yading@11
|
270 pkt->flags |= hdr[2] == 0 ? AV_PKT_FLAG_KEY : 0;
|
yading@11
|
271 pkt->pts = AV_RL32(&hdr[4]);
|
yading@11
|
272 pkt->stream_index = ctx->v_id;
|
yading@11
|
273 memcpy(pkt->data, hdr, copyhdrsize);
|
yading@11
|
274 ret = avio_read(pb, pkt->data + copyhdrsize, size);
|
yading@11
|
275 if (ret < 0) {
|
yading@11
|
276 av_free_packet(pkt);
|
yading@11
|
277 return ret;
|
yading@11
|
278 }
|
yading@11
|
279 if (ret < size)
|
yading@11
|
280 av_shrink_packet(pkt, copyhdrsize + ret);
|
yading@11
|
281 return 0;
|
yading@11
|
282 case NUV_AUDIO:
|
yading@11
|
283 if (ctx->a_id < 0) {
|
yading@11
|
284 av_log(s, AV_LOG_ERROR, "Audio packet in file without audio stream!\n");
|
yading@11
|
285 avio_skip(pb, size);
|
yading@11
|
286 break;
|
yading@11
|
287 }
|
yading@11
|
288 ret = av_get_packet(pb, pkt, size);
|
yading@11
|
289 pkt->flags |= AV_PKT_FLAG_KEY;
|
yading@11
|
290 pkt->pos = pos;
|
yading@11
|
291 pkt->pts = AV_RL32(&hdr[4]);
|
yading@11
|
292 pkt->stream_index = ctx->a_id;
|
yading@11
|
293 if (ret < 0)
|
yading@11
|
294 return ret;
|
yading@11
|
295 return 0;
|
yading@11
|
296 case NUV_SEEKP:
|
yading@11
|
297 // contains no data, size value is invalid
|
yading@11
|
298 break;
|
yading@11
|
299 default:
|
yading@11
|
300 avio_skip(pb, size);
|
yading@11
|
301 break;
|
yading@11
|
302 }
|
yading@11
|
303 }
|
yading@11
|
304
|
yading@11
|
305 return AVERROR(EIO);
|
yading@11
|
306 }
|
yading@11
|
307
|
yading@11
|
308 /**
|
yading@11
|
309 * \brief looks for the string RTjjjjjjjjjj in the stream too resync reading
|
yading@11
|
310 * \return 1 if the syncword is found 0 otherwise.
|
yading@11
|
311 */
|
yading@11
|
312 static int nuv_resync(AVFormatContext *s, int64_t pos_limit) {
|
yading@11
|
313 AVIOContext *pb = s->pb;
|
yading@11
|
314 uint32_t tag = 0;
|
yading@11
|
315 while(!url_feof(pb) && avio_tell(pb) < pos_limit) {
|
yading@11
|
316 tag = (tag << 8) | avio_r8(pb);
|
yading@11
|
317 if (tag == MKBETAG('R','T','j','j') &&
|
yading@11
|
318 (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') &&
|
yading@11
|
319 (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j'))
|
yading@11
|
320 return 1;
|
yading@11
|
321 }
|
yading@11
|
322 return 0;
|
yading@11
|
323 }
|
yading@11
|
324
|
yading@11
|
325 /**
|
yading@11
|
326 * \brief attempts to read a timestamp from stream at the given stream position
|
yading@11
|
327 * \return timestamp if successful and AV_NOPTS_VALUE if failure
|
yading@11
|
328 */
|
yading@11
|
329 static int64_t nuv_read_dts(AVFormatContext *s, int stream_index,
|
yading@11
|
330 int64_t *ppos, int64_t pos_limit)
|
yading@11
|
331 {
|
yading@11
|
332 NUVContext *ctx = s->priv_data;
|
yading@11
|
333 AVIOContext *pb = s->pb;
|
yading@11
|
334 uint8_t hdr[HDRSIZE];
|
yading@11
|
335 nuv_frametype frametype;
|
yading@11
|
336 int size, key, idx;
|
yading@11
|
337 int64_t pos, dts;
|
yading@11
|
338
|
yading@11
|
339 if (avio_seek(pb, *ppos, SEEK_SET) < 0)
|
yading@11
|
340 return AV_NOPTS_VALUE;
|
yading@11
|
341
|
yading@11
|
342 if (!nuv_resync(s, pos_limit))
|
yading@11
|
343 return AV_NOPTS_VALUE;
|
yading@11
|
344
|
yading@11
|
345 while (!url_feof(pb) && avio_tell(pb) < pos_limit) {
|
yading@11
|
346 if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE)
|
yading@11
|
347 return AV_NOPTS_VALUE;
|
yading@11
|
348 frametype = hdr[0];
|
yading@11
|
349 size = PKTSIZE(AV_RL32(&hdr[8]));
|
yading@11
|
350 switch (frametype) {
|
yading@11
|
351 case NUV_SEEKP:
|
yading@11
|
352 break;
|
yading@11
|
353 case NUV_AUDIO:
|
yading@11
|
354 case NUV_VIDEO:
|
yading@11
|
355 if (frametype == NUV_VIDEO) {
|
yading@11
|
356 idx = ctx->v_id;
|
yading@11
|
357 key = hdr[2] == 0;
|
yading@11
|
358 } else {
|
yading@11
|
359 idx = ctx->a_id;
|
yading@11
|
360 key = 1;
|
yading@11
|
361 }
|
yading@11
|
362 if (stream_index == idx) {
|
yading@11
|
363
|
yading@11
|
364 pos = avio_tell(s->pb) - HDRSIZE;
|
yading@11
|
365 dts = AV_RL32(&hdr[4]);
|
yading@11
|
366
|
yading@11
|
367 // TODO - add general support in av_gen_search, so it adds positions after reading timestamps
|
yading@11
|
368 av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0,
|
yading@11
|
369 key ? AVINDEX_KEYFRAME : 0);
|
yading@11
|
370
|
yading@11
|
371 *ppos = pos;
|
yading@11
|
372 return dts;
|
yading@11
|
373 }
|
yading@11
|
374 default:
|
yading@11
|
375 avio_skip(pb, size);
|
yading@11
|
376 break;
|
yading@11
|
377 }
|
yading@11
|
378 }
|
yading@11
|
379 return AV_NOPTS_VALUE;
|
yading@11
|
380 }
|
yading@11
|
381
|
yading@11
|
382
|
yading@11
|
383 AVInputFormat ff_nuv_demuxer = {
|
yading@11
|
384 .name = "nuv",
|
yading@11
|
385 .long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"),
|
yading@11
|
386 .priv_data_size = sizeof(NUVContext),
|
yading@11
|
387 .read_probe = nuv_probe,
|
yading@11
|
388 .read_header = nuv_header,
|
yading@11
|
389 .read_packet = nuv_packet,
|
yading@11
|
390 .read_timestamp = nuv_read_dts,
|
yading@11
|
391 .flags = AVFMT_GENERIC_INDEX,
|
yading@11
|
392 };
|