nuv.c 11.3 KB
Newer Older
1 2
/*
 * NuppelVideo demuxer.
3
 * Copyright (c) 2006 Reimar Doeffinger
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19 20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
21 22

#include "libavutil/intreadwrite.h"
23
#include "libavutil/intfloat.h"
24
#include "avformat.h"
25
#include "internal.h"
26
#include "riff.h"
27 28 29 30

typedef struct {
    int v_id;
    int a_id;
31
    int rtjpg_video;
32 33 34 35 36 37 38 39
} NUVContext;

typedef enum {
    NUV_VIDEO = 'V',
    NUV_EXTRADATA = 'D',
    NUV_AUDIO = 'A',
    NUV_SEEKP = 'R',
    NUV_MYTHEXT = 'X'
40
} nuv_frametype;
41 42 43 44 45 46 47 48 49

static int nuv_probe(AVProbeData *p) {
    if (!memcmp(p->buf, "NuppelVideo", 12))
        return AVPROBE_SCORE_MAX;
    if (!memcmp(p->buf, "MythTVVideo", 12))
        return AVPROBE_SCORE_MAX;
    return 0;
}

50
/// little macro to sanitize packet size
51 52 53
#define PKTSIZE(s) (s &  0xffffff)

/**
54 55 56 57 58
 * @brief read until we found all data needed for decoding
 * @param vst video stream of which to change parameters
 * @param ast video stream of which to change parameters
 * @param myth set if this is a MythTVVideo format file
 * @return 1 if all required codec data was found
59
 */
60
static int get_codec_data(AVIOContext *pb, AVStream *vst,
61
                          AVStream *ast, int myth) {
62
    nuv_frametype frametype;
63 64 65 66
    if (!vst && !myth)
        return 1; // no codec data needed
    while (!url_feof(pb)) {
        int size, subtype;
67
        frametype = avio_r8(pb);
68 69
        switch (frametype) {
            case NUV_EXTRADATA:
70
                subtype = avio_r8(pb);
71
                avio_skip(pb, 6);
72
                size = PKTSIZE(avio_rl32(pb));
73
                if (vst && subtype == 'R') {
74 75
                    vst->codec->extradata_size = size;
                    vst->codec->extradata = av_malloc(size);
76
                    avio_read(pb, vst->codec->extradata, size);
77 78 79 80 81 82
                    size = 0;
                    if (!myth)
                        return 1;
                }
                break;
            case NUV_MYTHEXT:
83
                avio_skip(pb, 7);
84
                size = PKTSIZE(avio_rl32(pb));
85 86
                if (size != 128 * 4)
                    break;
87
                avio_rl32(pb); // version
88
                if (vst) {
89
                    vst->codec->codec_tag = avio_rl32(pb);
90
                    vst->codec->codec_id =
91
                        ff_codec_get_id(ff_codec_bmp_tags, vst->codec->codec_tag);
92 93
                    if (vst->codec->codec_tag == MKTAG('R', 'J', 'P', 'G'))
                        vst->codec->codec_id = CODEC_ID_NUV;
94
                } else
95
                    avio_skip(pb, 4);
96 97

                if (ast) {
98 99 100 101
                    ast->codec->codec_tag = avio_rl32(pb);
                    ast->codec->sample_rate = avio_rl32(pb);
                    ast->codec->bits_per_coded_sample = avio_rl32(pb);
                    ast->codec->channels = avio_rl32(pb);
102
                    ast->codec->codec_id =
103
                        ff_wav_codec_get_id(ast->codec->codec_tag,
104
                                         ast->codec->bits_per_coded_sample);
105
                    ast->need_parsing = AVSTREAM_PARSE_FULL;
106
                } else
107
                    avio_skip(pb, 4 * 4);
108 109

                size -= 6 * 4;
110
                avio_skip(pb, size);
111 112 113 114 115
                return 1;
            case NUV_SEEKP:
                size = 11;
                break;
            default:
116
                avio_skip(pb, 7);
117
                size = PKTSIZE(avio_rl32(pb));
118 119
                break;
        }
120
        avio_skip(pb, size);
121 122 123 124
    }
    return 0;
}

125
static int nuv_header(AVFormatContext *s) {
126
    NUVContext *ctx = s->priv_data;
127
    AVIOContext *pb = s->pb;
128
    char id_string[12];
129 130 131 132
    double aspect, fps;
    int is_mythtv, width, height, v_packs, a_packs;
    int stream_nr = 0;
    AVStream *vst = NULL, *ast = NULL;
133
    avio_read(pb, id_string, 12);
134
    is_mythtv = !memcmp(id_string, "MythTVVideo", 12);
135 136
    avio_skip(pb, 5); // version string
    avio_skip(pb, 3); // padding
137 138 139 140 141
    width = avio_rl32(pb);
    height = avio_rl32(pb);
    avio_rl32(pb); // unused, "desiredwidth"
    avio_rl32(pb); // unused, "desiredheight"
    avio_r8(pb); // 'P' == progressive, 'I' == interlaced
142
    avio_skip(pb, 3); // padding
143
    aspect = av_int2double(avio_rl64(pb));
144 145
    if (aspect > 0.9999 && aspect < 1.0001)
        aspect = 4.0 / 3.0;
146
    fps = av_int2double(avio_rl64(pb));
147 148

    // number of packets per stream type, -1 means unknown, e.g. streaming
149 150 151
    v_packs = avio_rl32(pb);
    a_packs = avio_rl32(pb);
    avio_rl32(pb); // text
152

153
    avio_rl32(pb); // keyframe distance (?)
154 155 156

    if (v_packs) {
        ctx->v_id = stream_nr++;
157
        vst = avformat_new_stream(s, NULL);
158 159
        if (!vst)
            return AVERROR(ENOMEM);
160
        vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
161 162 163
        vst->codec->codec_id = CODEC_ID_NUV;
        vst->codec->width = width;
        vst->codec->height = height;
164
        vst->codec->bits_per_coded_sample = 10;
165
        vst->sample_aspect_ratio = av_d2q(aspect * height / width, 10000);
166
        vst->r_frame_rate = av_d2q(fps, 60000);
167
        avpriv_set_pts_info(vst, 32, 1, 1000);
168 169 170 171 172
    } else
        ctx->v_id = -1;

    if (a_packs) {
        ctx->a_id = stream_nr++;
173
        ast = avformat_new_stream(s, NULL);
174 175
        if (!ast)
            return AVERROR(ENOMEM);
176
        ast->codec->codec_type = AVMEDIA_TYPE_AUDIO;
177 178 179 180 181
        ast->codec->codec_id = CODEC_ID_PCM_S16LE;
        ast->codec->channels = 2;
        ast->codec->sample_rate = 44100;
        ast->codec->bit_rate = 2 * 2 * 44100 * 8;
        ast->codec->block_align = 2 * 2;
182
        ast->codec->bits_per_coded_sample = 16;
183
        avpriv_set_pts_info(ast, 32, 1, 1000);
184 185 186 187
    } else
        ctx->a_id = -1;

    get_codec_data(pb, vst, ast, is_mythtv);
188
    ctx->rtjpg_video = vst && vst->codec->codec_id == CODEC_ID_NUV;
189 190 191 192 193 194
    return 0;
}

#define HDRSIZE 12

static int nuv_packet(AVFormatContext *s, AVPacket *pkt) {
195
    NUVContext *ctx = s->priv_data;
196
    AVIOContext *pb = s->pb;
197
    uint8_t hdr[HDRSIZE];
198
    nuv_frametype frametype;
199 200
    int ret, size;
    while (!url_feof(pb)) {
201
        int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0;
202
        uint64_t pos = avio_tell(pb);
203
        ret = avio_read(pb, hdr, HDRSIZE);
204 205
        if (ret < HDRSIZE)
            return ret < 0 ? ret : AVERROR(EIO);
206
        frametype = hdr[0];
207
        size = PKTSIZE(AV_RL32(&hdr[8]));
208 209
        switch (frametype) {
            case NUV_EXTRADATA:
210
                if (!ctx->rtjpg_video) {
211
                    avio_skip(pb, size);
212 213 214
                    break;
                }
            case NUV_VIDEO:
215 216
                if (ctx->v_id < 0) {
                    av_log(s, AV_LOG_ERROR, "Video packet in file without video stream!\n");
217
                    avio_skip(pb, size);
218 219
                    break;
                }
220
                ret = av_new_packet(pkt, copyhdrsize + size);
221 222
                if (ret < 0)
                    return ret;
223

224
                pkt->pos = pos;
225
                pkt->flags |= hdr[2] == 0 ? AV_PKT_FLAG_KEY : 0;
226
                pkt->pts = AV_RL32(&hdr[4]);
227
                pkt->stream_index = ctx->v_id;
228
                memcpy(pkt->data, hdr, copyhdrsize);
229
                ret = avio_read(pb, pkt->data + copyhdrsize, size);
230 231 232 233
                if (ret < 0) {
                    av_free_packet(pkt);
                    return ret;
                }
234 235
                if (ret < size)
                    av_shrink_packet(pkt, copyhdrsize + ret);
236
                return 0;
237 238 239
            case NUV_AUDIO:
                if (ctx->a_id < 0) {
                    av_log(s, AV_LOG_ERROR, "Audio packet in file without audio stream!\n");
240
                    avio_skip(pb, size);
241 242 243
                    break;
                }
                ret = av_get_packet(pb, pkt, size);
244
                pkt->flags |= AV_PKT_FLAG_KEY;
245
                pkt->pos = pos;
246
                pkt->pts = AV_RL32(&hdr[4]);
247
                pkt->stream_index = ctx->a_id;
248 249
                if (ret < 0) return ret;
                return 0;
250 251 252 253
            case NUV_SEEKP:
                // contains no data, size value is invalid
                break;
            default:
254
                avio_skip(pb, size);
255 256 257
                break;
        }
    }
258
    return AVERROR(EIO);
259 260
}

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
/**
 * \brief looks for the string RTjjjjjjjjjj in the stream too resync reading
 * \return 1 if the syncword is found 0 otherwise.
 */
static int nuv_resync(AVFormatContext *s, int64_t pos_limit) {
    AVIOContext *pb = s->pb;
    uint32_t tag = 0;
    while(!url_feof(pb) && avio_tell(pb) < pos_limit) {
        tag = (tag << 8) | avio_r8(pb);
        if (tag                  == MKBETAG('R','T','j','j') &&
           (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j') &&
           (tag = avio_rb32(pb)) == MKBETAG('j','j','j','j'))
            return 1;
    }
    return 0;
}

/**
 * \brief attempts to read a timestamp from stream at the given stream position
Lou Logan's avatar
Lou Logan committed
280
 * \return timestamp if successful and AV_NOPTS_VALUE if failure
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
 */
static int64_t nuv_read_dts(AVFormatContext *s, int stream_index,
                            int64_t *ppos, int64_t pos_limit)
{
    NUVContext *ctx = s->priv_data;
    AVIOContext *pb = s->pb;
    uint8_t hdr[HDRSIZE];
    nuv_frametype frametype;
    int size, key, idx;
    int64_t pos, dts;

    if (avio_seek(pb, *ppos, SEEK_SET) < 0)
        return AV_NOPTS_VALUE;

    if (!nuv_resync(s, pos_limit))
        return AV_NOPTS_VALUE;

    while (!url_feof(pb) && avio_tell(pb) < pos_limit) {
        if (avio_read(pb, hdr, HDRSIZE) < HDRSIZE)
            return AV_NOPTS_VALUE;
        frametype = hdr[0];
        size = PKTSIZE(AV_RL32(&hdr[8]));
        switch (frametype) {
            case NUV_SEEKP:
                break;
            case NUV_AUDIO:
            case NUV_VIDEO:
                if (frametype == NUV_VIDEO) {
                    idx = ctx->v_id;
                    key = hdr[2] == 0;
                } else {
                    idx = ctx->a_id;
                    key = 1;
                }
                if (stream_index == idx) {

                    pos = avio_tell(s->pb) - HDRSIZE;
                    dts = AV_RL32(&hdr[4]);

                    // TODO - add general support in av_gen_search, so it adds positions after reading timestamps
                    av_add_index_entry(s->streams[stream_index], pos, dts, size + HDRSIZE, 0,
                            key ? AVINDEX_KEYFRAME : 0);

                    *ppos = pos;
                    return dts;
                }
            default:
                avio_skip(pb, size);
                break;
        }
    }
    return AV_NOPTS_VALUE;
}


336
AVInputFormat ff_nuv_demuxer = {
337 338 339 340 341 342
    .name           = "nuv",
    .long_name      = NULL_IF_CONFIG_SMALL("NuppelVideo format"),
    .priv_data_size = sizeof(NUVContext),
    .read_probe     = nuv_probe,
    .read_header    = nuv_header,
    .read_packet    = nuv_packet,
343
    .read_timestamp = nuv_read_dts,
344
    .flags          = AVFMT_GENERIC_INDEX,
345
};