00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/channel_layout.h"
00023 #include "libavutil/intreadwrite.h"
00024 #include "libavutil/intfloat.h"
00025 #include "avformat.h"
00026 #include "internal.h"
00027 #include "riff.h"
00028
00029 static const AVCodecTag nuv_audio_tags[] = {
00030 { AV_CODEC_ID_PCM_S16LE, MKTAG('R', 'A', 'W', 'A') },
00031 { AV_CODEC_ID_MP3, MKTAG('L', 'A', 'M', 'E') },
00032 { AV_CODEC_ID_NONE, 0 },
00033 };
00034
00035 typedef struct {
00036 int v_id;
00037 int a_id;
00038 int rtjpg_video;
00039 } NUVContext;
00040
00041 typedef enum {
00042 NUV_VIDEO = 'V',
00043 NUV_EXTRADATA = 'D',
00044 NUV_AUDIO = 'A',
00045 NUV_SEEKP = 'R',
00046 NUV_MYTHEXT = 'X'
00047 } nuv_frametype;
00048
00049 static int nuv_probe(AVProbeData *p)
00050 {
00051 if (!memcmp(p->buf, "NuppelVideo", 12))
00052 return AVPROBE_SCORE_MAX;
00053 if (!memcmp(p->buf, "MythTVVideo", 12))
00054 return AVPROBE_SCORE_MAX;
00055 return 0;
00056 }
00057
00059 #define PKTSIZE(s) (s & 0xffffff)
00060
00068 static int get_codec_data(AVIOContext *pb, AVStream *vst,
00069 AVStream *ast, int myth)
00070 {
00071 nuv_frametype frametype;
00072
00073 if (!vst && !myth)
00074 return 1;
00075 while (!url_feof(pb)) {
00076 int size, subtype;
00077
00078 frametype = avio_r8(pb);
00079 switch (frametype) {
00080 case NUV_EXTRADATA:
00081 subtype = avio_r8(pb);
00082 avio_skip(pb, 6);
00083 size = PKTSIZE(avio_rl32(pb));
00084 if (vst && subtype == 'R') {
00085 if (vst->codec->extradata) {
00086 av_freep(&vst->codec->extradata);
00087 vst->codec->extradata_size = 0;
00088 }
00089 vst->codec->extradata = av_malloc(size);
00090 if (!vst->codec->extradata)
00091 return AVERROR(ENOMEM);
00092 vst->codec->extradata_size = size;
00093 avio_read(pb, vst->codec->extradata, size);
00094 size = 0;
00095 if (!myth)
00096 return 0;
00097 }
00098 break;
00099 case NUV_MYTHEXT:
00100 avio_skip(pb, 7);
00101 size = PKTSIZE(avio_rl32(pb));
00102 if (size != 128 * 4)
00103 break;
00104 avio_rl32(pb);
00105 if (vst) {
00106 vst->codec->codec_tag = avio_rl32(pb);
00107 vst->codec->codec_id =
00108 ff_codec_get_id(ff_codec_bmp_tags, vst->codec->codec_tag);
00109 if (vst->codec->codec_tag == MKTAG('R', 'J', 'P', 'G'))
00110 vst->codec->codec_id = AV_CODEC_ID_NUV;
00111 } else
00112 avio_skip(pb, 4);
00113
00114 if (ast) {
00115 int id;
00116
00117 ast->codec->codec_tag = avio_rl32(pb);
00118 ast->codec->sample_rate = avio_rl32(pb);
00119 ast->codec->bits_per_coded_sample = avio_rl32(pb);
00120 ast->codec->channels = avio_rl32(pb);
00121 ast->codec->channel_layout = 0;
00122
00123 id = ff_wav_codec_get_id(ast->codec->codec_tag,
00124 ast->codec->bits_per_coded_sample);
00125 if (id == AV_CODEC_ID_NONE) {
00126 id = ff_codec_get_id(nuv_audio_tags, ast->codec->codec_tag);
00127 if (id == AV_CODEC_ID_PCM_S16LE)
00128 id = ff_get_pcm_codec_id(ast->codec->bits_per_coded_sample,
00129 0, 0, ~1);
00130 }
00131 ast->codec->codec_id = id;
00132
00133 ast->need_parsing = AVSTREAM_PARSE_FULL;
00134 } else
00135 avio_skip(pb, 4 * 4);
00136
00137 size -= 6 * 4;
00138 avio_skip(pb, size);
00139 return 0;
00140 case NUV_SEEKP:
00141 size = 11;
00142 break;
00143 default:
00144 avio_skip(pb, 7);
00145 size = PKTSIZE(avio_rl32(pb));
00146 break;
00147 }
00148 avio_skip(pb, size);
00149 }
00150
00151 return 0;
00152 }
00153
00154 static int nuv_header(AVFormatContext *s)
00155 {
00156 NUVContext *ctx = s->priv_data;
00157 AVIOContext *pb = s->pb;
00158 char id_string[12];
00159 double aspect, fps;
00160 int is_mythtv, width, height, v_packs, a_packs, ret;
00161 AVStream *vst = NULL, *ast = NULL;
00162
00163 avio_read(pb, id_string, 12);
00164 is_mythtv = !memcmp(id_string, "MythTVVideo", 12);
00165 avio_skip(pb, 5);
00166 avio_skip(pb, 3);
00167 width = avio_rl32(pb);
00168 height = avio_rl32(pb);
00169 avio_rl32(pb);
00170 avio_rl32(pb);
00171 avio_r8(pb);
00172 avio_skip(pb, 3);
00173 aspect = av_int2double(avio_rl64(pb));
00174 if (aspect > 0.9999 && aspect < 1.0001)
00175 aspect = 4.0 / 3.0;
00176 fps = av_int2double(avio_rl64(pb));
00177
00178
00179 v_packs = avio_rl32(pb);
00180 a_packs = avio_rl32(pb);
00181 avio_rl32(pb);
00182
00183 avio_rl32(pb);
00184
00185 if (v_packs) {
00186 vst = avformat_new_stream(s, NULL);
00187 if (!vst)
00188 return AVERROR(ENOMEM);
00189 ctx->v_id = vst->index;
00190
00191 vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00192 vst->codec->codec_id = AV_CODEC_ID_NUV;
00193 vst->codec->width = width;
00194 vst->codec->height = height;
00195 vst->codec->bits_per_coded_sample = 10;
00196 vst->sample_aspect_ratio = av_d2q(aspect * height / width,
00197 10000);
00198 #if FF_API_R_FRAME_RATE
00199 vst->r_frame_rate =
00200 #endif
00201 vst->avg_frame_rate = av_d2q(fps, 60000);
00202 avpriv_set_pts_info(vst, 32, 1, 1000);
00203 } else
00204 ctx->v_id = -1;
00205
00206 if (a_packs) {
00207 ast = avformat_new_stream(s, NULL);
00208 if (!ast)
00209 return AVERROR(ENOMEM);
00210 ctx->a_id = ast->index;
00211
00212 ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
00213 ast->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
00214 ast->codec->channels = 2;
00215 ast->codec->channel_layout = AV_CH_LAYOUT_STEREO;
00216 ast->codec->sample_rate = 44100;
00217 ast->codec->bit_rate = 2 * 2 * 44100 * 8;
00218 ast->codec->block_align = 2 * 2;
00219 ast->codec->bits_per_coded_sample = 16;
00220 avpriv_set_pts_info(ast, 32, 1, 1000);
00221 } else
00222 ctx->a_id = -1;
00223
00224 if ((ret = get_codec_data(pb, vst, ast, is_mythtv)) < 0)
00225 return ret;
00226
00227 ctx->rtjpg_video = vst && vst->codec->codec_id == AV_CODEC_ID_NUV;
00228
00229 return 0;
00230 }
00231
00232 #define HDRSIZE 12
00233
00234 static int nuv_packet(AVFormatContext *s, AVPacket *pkt)
00235 {
00236 NUVContext *ctx = s->priv_data;
00237 AVIOContext *pb = s->pb;
00238 uint8_t hdr[HDRSIZE];
00239 nuv_frametype frametype;
00240 int ret, size;
00241
00242 while (!url_feof(pb)) {
00243 int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0;
00244 uint64_t pos = avio_tell(pb);
00245
00246 ret = avio_read(pb, hdr, HDRSIZE);
00247 if (ret < HDRSIZE)
00248 return ret < 0 ? ret : AVERROR(EIO);
00249
00250 frametype = hdr[0];
00251 size = PKTSIZE(AV_RL32(&hdr[8]));
00252
00253 switch (frametype) {
00254 case NUV_EXTRADATA:
00255 if (!ctx->rtjpg_video) {
00256 avio_skip(pb, size);
00257 break;
00258 }
00259 case NUV_VIDEO:
00260 if (ctx->v_id < 0) {
00261 av_log(s, AV_LOG_ERROR, "Video packet in file without video stream!\n");
00262 avio_skip(pb, size);
00263 break;
00264 }
00265 ret = av_new_packet(pkt, copyhdrsize + size);
00266 if (ret < 0)
00267 return ret;
00268
00269 pkt->pos = pos;
00270 pkt->flags |= hdr[2] == 0 ? AV_PKT_FLAG_KEY : 0;
00271 pkt->pts = AV_RL32(&hdr[4]);
00272 pkt->stream_index = ctx->v_id;
00273 memcpy(pkt->data, hdr, copyhdrsize);
00274 ret = avio_read(pb, pkt->data + copyhdrsize, size);
00275 if (ret < 0) {
00276 av_free_packet(pkt);
00277 return ret;
00278 }
00279 if (ret < size)
00280 av_shrink_packet(pkt, copyhdrsize + ret);
00281 return 0;
00282 case NUV_AUDIO:
00283 if (ctx->a_id < 0) {
00284 av_log(s, AV_LOG_ERROR, "Audio packet in file without audio stream!\n");
00285 avio_skip(pb, size);
00286 break;
00287 }
00288 ret = av_get_packet(pb, pkt, size);
00289 pkt->flags |= AV_PKT_FLAG_KEY;
00290 pkt->pos = pos;
00291 pkt->pts = AV_RL32(&hdr[4]);
00292 pkt->stream_index = ctx->a_id;
00293 if (ret < 0)
00294 return ret;
00295 return 0;
00296 case NUV_SEEKP:
00297
00298 break;
00299 default:
00300 avio_skip(pb, size);
00301 break;
00302 }
00303 }
00304
00305 return AVERROR(EIO);
00306 }
00307
00312 static int nuv_resync(AVFormatContext *s, int64_t pos_limit) {
00313 AVIOContext *pb = s->pb;
00314 uint32_t tag = 0;
00315 while(!url_feof(pb) && avio_tell(pb) < pos_limit) {
00316 tag = (tag << 8) | avio_r8(pb);
00317 if (tag == MKBETAG('R','T','j','j') &&
00318 (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') &&
00319 (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j'))
00320 return 1;
00321 }
00322 return 0;
00323 }
00324
00329 static int64_t nuv_read_dts(AVFormatContext *s, int stream_index,
00330 int64_t *ppos, int64_t pos_limit)
00331 {
00332 NUVContext *ctx = s->priv_data;
00333 AVIOContext *pb = s->pb;
00334 uint8_t hdr[HDRSIZE];
00335 nuv_frametype frametype;
00336 int size, key, idx;
00337 int64_t pos, dts;
00338
00339 if (avio_seek(pb, *ppos, SEEK_SET) < 0)
00340 return AV_NOPTS_VALUE;
00341
00342 if (!nuv_resync(s, pos_limit))
00343 return AV_NOPTS_VALUE;
00344
00345 while (!url_feof(pb) && avio_tell(pb) < pos_limit) {
00346 if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE)
00347 return AV_NOPTS_VALUE;
00348 frametype = hdr[0];
00349 size = PKTSIZE(AV_RL32(&hdr[8]));
00350 switch (frametype) {
00351 case NUV_SEEKP:
00352 break;
00353 case NUV_AUDIO:
00354 case NUV_VIDEO:
00355 if (frametype == NUV_VIDEO) {
00356 idx = ctx->v_id;
00357 key = hdr[2] == 0;
00358 } else {
00359 idx = ctx->a_id;
00360 key = 1;
00361 }
00362 if (stream_index == idx) {
00363
00364 pos = avio_tell(s->pb) - HDRSIZE;
00365 dts = AV_RL32(&hdr[4]);
00366
00367
00368 av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0,
00369 key ? AVINDEX_KEYFRAME : 0);
00370
00371 *ppos = pos;
00372 return dts;
00373 }
00374 default:
00375 avio_skip(pb, size);
00376 break;
00377 }
00378 }
00379 return AV_NOPTS_VALUE;
00380 }
00381
00382
00383 AVInputFormat ff_nuv_demuxer = {
00384 .name = "nuv",
00385 .long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"),
00386 .priv_data_size = sizeof(NUVContext),
00387 .read_probe = nuv_probe,
00388 .read_header = nuv_header,
00389 .read_packet = nuv_packet,
00390 .read_timestamp = nuv_read_dts,
00391 .flags = AVFMT_GENERIC_INDEX,
00392 };