nuv.c 10 KB
Newer Older
1 2
/*
 * NuppelVideo demuxer.
3
 * Copyright (c) 2006 Reimar Doeffinger
4
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav 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
 * Libav 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 Libav; if not, write to the Free Software
19 20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
21

Justin Ruggles's avatar
Justin Ruggles committed
22
#include "libavutil/channel_layout.h"
23
#include "libavutil/intreadwrite.h"
24
#include "libavutil/intfloat.h"
25
#include "avformat.h"
26
#include "internal.h"
27
#include "riff.h"
28

29 30 31 32 33 34
static const AVCodecTag nuv_audio_tags[] = {
    { AV_CODEC_ID_PCM_S16LE, MKTAG('R', 'A', 'W', 'A') },
    { AV_CODEC_ID_MP3,       MKTAG('L', 'A', 'M', 'E') },
    { AV_CODEC_ID_NONE,      0 },
};

35 36 37
typedef struct {
    int v_id;
    int a_id;
38
    int rtjpg_video;
39 40 41
} NUVContext;

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

49 50
static int nuv_probe(AVProbeData *p)
{
51 52 53 54 55 56 57
    if (!memcmp(p->buf, "NuppelVideo", 12))
        return AVPROBE_SCORE_MAX;
    if (!memcmp(p->buf, "MythTVVideo", 12))
        return AVPROBE_SCORE_MAX;
    return 0;
}

58
/// little macro to sanitize packet size
59 60 61
#define PKTSIZE(s) (s &  0xffffff)

/**
62 63 64 65
 * @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
66
 * @return 0 or AVERROR code
67
 */
68
static int get_codec_data(AVIOContext *pb, AVStream *vst,
69 70
                          AVStream *ast, int myth)
{
71
    nuv_frametype frametype;
72

73 74
    if (!vst && !myth)
        return 1; // no codec data needed
Anton Khirnov's avatar
Anton Khirnov committed
75
    while (!pb->eof_reached) {
76
        int size, subtype;
77

78
        frametype = avio_r8(pb);
79
        switch (frametype) {
80 81 82 83 84
        case NUV_EXTRADATA:
            subtype = avio_r8(pb);
            avio_skip(pb, 6);
            size = PKTSIZE(avio_rl32(pb));
            if (vst && subtype == 'R') {
85 86 87 88 89 90 91
                if (vst->codec->extradata) {
                    av_freep(&vst->codec->extradata);
                    vst->codec->extradata_size = 0;
                }
                vst->codec->extradata = av_malloc(size);
                if (!vst->codec->extradata)
                    return AVERROR(ENOMEM);
92 93 94 95
                vst->codec->extradata_size = size;
                avio_read(pb, vst->codec->extradata, size);
                size = 0;
                if (!myth)
96
                    return 0;
97 98 99 100 101 102
            }
            break;
        case NUV_MYTHEXT:
            avio_skip(pb, 7);
            size = PKTSIZE(avio_rl32(pb));
            if (size != 128 * 4)
103
                break;
104 105 106 107 108 109 110 111 112
            avio_rl32(pb); // version
            if (vst) {
                vst->codec->codec_tag = avio_rl32(pb);
                vst->codec->codec_id =
                    ff_codec_get_id(ff_codec_bmp_tags, vst->codec->codec_tag);
                if (vst->codec->codec_tag == MKTAG('R', 'J', 'P', 'G'))
                    vst->codec->codec_id = AV_CODEC_ID_NUV;
            } else
                avio_skip(pb, 4);
113

114 115
            if (ast) {
                int id;
116

117 118 119 120 121
                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);
                ast->codec->channel_layout        = 0;
122

123 124 125 126 127 128 129 130 131
                id = ff_wav_codec_get_id(ast->codec->codec_tag,
                                         ast->codec->bits_per_coded_sample);
                if (id == AV_CODEC_ID_NONE) {
                    id = ff_codec_get_id(nuv_audio_tags, ast->codec->codec_tag);
                    if (id == AV_CODEC_ID_PCM_S16LE)
                        id = ff_get_pcm_codec_id(ast->codec->bits_per_coded_sample,
                                                 0, 0, ~1);
                }
                ast->codec->codec_id = id;
132

133 134 135
                ast->need_parsing = AVSTREAM_PARSE_FULL;
            } else
                avio_skip(pb, 4 * 4);
136

137 138
            size -= 6 * 4;
            avio_skip(pb, size);
139
            return 0;
140 141 142 143 144 145 146
        case NUV_SEEKP:
            size = 11;
            break;
        default:
            avio_skip(pb, 7);
            size = PKTSIZE(avio_rl32(pb));
            break;
147
        }
148
        avio_skip(pb, size);
149
    }
150

151 152 153
    return 0;
}

154 155
static int nuv_header(AVFormatContext *s)
{
156
    NUVContext *ctx = s->priv_data;
157
    AVIOContext *pb = s->pb;
158
    char id_string[12];
159
    double aspect, fps;
160
    int is_mythtv, width, height, v_packs, a_packs, ret;
161
    AVStream *vst = NULL, *ast = NULL;
162

163
    avio_read(pb, id_string, 12);
164
    is_mythtv = !memcmp(id_string, "MythTVVideo", 12);
165 166 167
    avio_skip(pb, 5);       // version string
    avio_skip(pb, 3);       // padding
    width  = avio_rl32(pb);
168
    height = avio_rl32(pb);
169 170 171 172
    avio_rl32(pb);          // unused, "desiredwidth"
    avio_rl32(pb);          // unused, "desiredheight"
    avio_r8(pb);            // 'P' == progressive, 'I' == interlaced
    avio_skip(pb, 3);       // padding
173
    aspect = av_int2double(avio_rl64(pb));
174 175
    if (aspect > 0.9999 && aspect < 1.0001)
        aspect = 4.0 / 3.0;
176
    fps = av_int2double(avio_rl64(pb));
177 178

    // number of packets per stream type, -1 means unknown, e.g. streaming
179 180 181
    v_packs = avio_rl32(pb);
    a_packs = avio_rl32(pb);
    avio_rl32(pb); // text
182

183
    avio_rl32(pb); // keyframe distance (?)
184 185

    if (v_packs) {
186
        vst = avformat_new_stream(s, NULL);
187 188
        if (!vst)
            return AVERROR(ENOMEM);
189 190
        ctx->v_id = vst->index;

191 192 193 194
        vst->codec->codec_type            = AVMEDIA_TYPE_VIDEO;
        vst->codec->codec_id              = AV_CODEC_ID_NUV;
        vst->codec->width                 = width;
        vst->codec->height                = height;
195
        vst->codec->bits_per_coded_sample = 10;
196 197
        vst->sample_aspect_ratio          = av_d2q(aspect * height / width,
                                                   10000);
198
        vst->avg_frame_rate = av_d2q(fps, 60000);
199
        avpriv_set_pts_info(vst, 32, 1, 1000);
200 201 202 203
    } else
        ctx->v_id = -1;

    if (a_packs) {
204
        ast = avformat_new_stream(s, NULL);
205 206
        if (!ast)
            return AVERROR(ENOMEM);
207 208
        ctx->a_id = ast->index;

209 210 211 212 213 214 215
        ast->codec->codec_type            = AVMEDIA_TYPE_AUDIO;
        ast->codec->codec_id              = AV_CODEC_ID_PCM_S16LE;
        ast->codec->channels              = 2;
        ast->codec->channel_layout        = AV_CH_LAYOUT_STEREO;
        ast->codec->sample_rate           = 44100;
        ast->codec->bit_rate              = 2 * 2 * 44100 * 8;
        ast->codec->block_align           = 2 * 2;
216
        ast->codec->bits_per_coded_sample = 16;
217
        avpriv_set_pts_info(ast, 32, 1, 1000);
218 219 220
    } else
        ctx->a_id = -1;

221 222 223
    if ((ret = get_codec_data(pb, vst, ast, is_mythtv)) < 0)
        return ret;

224
    ctx->rtjpg_video = vst && vst->codec->codec_id == AV_CODEC_ID_NUV;
225

226 227 228 229 230
    return 0;
}

#define HDRSIZE 12

231 232
static int nuv_packet(AVFormatContext *s, AVPacket *pkt)
{
233
    NUVContext *ctx = s->priv_data;
234
    AVIOContext *pb = s->pb;
235
    uint8_t hdr[HDRSIZE];
236
    nuv_frametype frametype;
237
    int ret, size;
238

Anton Khirnov's avatar
Anton Khirnov committed
239
    while (!pb->eof_reached) {
240
        int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0;
241 242
        uint64_t pos    = avio_tell(pb);

243
        ret = avio_read(pb, hdr, HDRSIZE);
244 245
        if (ret < HDRSIZE)
            return ret < 0 ? ret : AVERROR(EIO);
246

247
        frametype = hdr[0];
248 249
        size      = PKTSIZE(AV_RL32(&hdr[8]));

250
        switch (frametype) {
251 252 253
        case NUV_EXTRADATA:
            if (!ctx->rtjpg_video) {
                avio_skip(pb, size);
254
                break;
255 256 257 258
            }
        case NUV_VIDEO:
            if (ctx->v_id < 0) {
                av_log(s, AV_LOG_ERROR, "Video packet in file without video stream!\n");
259
                avio_skip(pb, size);
260
                break;
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
            }
            ret = av_new_packet(pkt, copyhdrsize + size);
            if (ret < 0)
                return ret;
            // HACK: we have no idea if it is a keyframe,
            // but if we mark none seeking will not work at all.
            pkt->flags       |= AV_PKT_FLAG_KEY;
            pkt->pos          = pos;
            pkt->pts          = AV_RL32(&hdr[4]);
            pkt->stream_index = ctx->v_id;
            memcpy(pkt->data, hdr, copyhdrsize);
            ret = avio_read(pb, pkt->data + copyhdrsize, size);
            if (ret < 0) {
                av_free_packet(pkt);
                return ret;
            }
            if (ret < size)
                av_shrink_packet(pkt, copyhdrsize + ret);
            return 0;
        case NUV_AUDIO:
            if (ctx->a_id < 0) {
                av_log(s, AV_LOG_ERROR, "Audio packet in file without audio stream!\n");
                avio_skip(pb, size);
                break;
            }
            ret               = av_get_packet(pb, pkt, size);
            pkt->flags       |= AV_PKT_FLAG_KEY;
            pkt->pos          = pos;
            pkt->pts          = AV_RL32(&hdr[4]);
            pkt->stream_index = ctx->a_id;
            if (ret < 0)
                return ret;
            return 0;
        case NUV_SEEKP:
            // contains no data, size value is invalid
            break;
        default:
            avio_skip(pb, size);
            break;
300 301
        }
    }
302

303
    return AVERROR(EIO);
304 305
}

306
AVInputFormat ff_nuv_demuxer = {
307
    .name           = "nuv",
308
    .long_name      = NULL_IF_CONFIG_SMALL("NuppelVideo"),
309 310 311 312
    .priv_data_size = sizeof(NUVContext),
    .read_probe     = nuv_probe,
    .read_header    = nuv_header,
    .read_packet    = nuv_packet,
313
    .flags          = AVFMT_GENERIC_INDEX,
314
};