1/* 2 * NuppelVideo demuxer. 3 * Copyright (c) 2006 Reimar Doeffinger 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * 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 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * 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 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include "libavutil/intreadwrite.h" 23#include "avformat.h" 24#include "riff.h" 25 26typedef struct { 27 int v_id; 28 int a_id; 29 int rtjpg_video; 30} NUVContext; 31 32typedef enum { 33 NUV_VIDEO = 'V', 34 NUV_EXTRADATA = 'D', 35 NUV_AUDIO = 'A', 36 NUV_SEEKP = 'R', 37 NUV_MYTHEXT = 'X' 38} nuv_frametype; 39 40static int nuv_probe(AVProbeData *p) { 41 if (!memcmp(p->buf, "NuppelVideo", 12)) 42 return AVPROBE_SCORE_MAX; 43 if (!memcmp(p->buf, "MythTVVideo", 12)) 44 return AVPROBE_SCORE_MAX; 45 return 0; 46} 47 48//! little macro to sanitize packet size 49#define PKTSIZE(s) (s & 0xffffff) 50 51/** 52 * \brief read until we found all data needed for decoding 53 * \param vst video stream of which to change parameters 54 * \param ast video stream of which to change parameters 55 * \param myth set if this is a MythTVVideo format file 56 * \return 1 if all required codec data was found 57 */ 58static int get_codec_data(ByteIOContext *pb, AVStream *vst, 59 AVStream *ast, int myth) { 60 nuv_frametype frametype; 61 if (!vst && !myth) 62 return 1; // no codec data needed 63 while (!url_feof(pb)) { 64 int size, subtype; 65 frametype = get_byte(pb); 66 switch (frametype) { 67 case NUV_EXTRADATA: 68 subtype = get_byte(pb); 69 url_fskip(pb, 6); 70 size = PKTSIZE(get_le32(pb)); 71 if (vst && subtype == 'R') { 72 vst->codec->extradata_size = size; 73 vst->codec->extradata = av_malloc(size); 74 get_buffer(pb, vst->codec->extradata, size); 75 size = 0; 76 if (!myth) 77 return 1; 78 } 79 break; 80 case NUV_MYTHEXT: 81 url_fskip(pb, 7); 82 size = PKTSIZE(get_le32(pb)); 83 if (size != 128 * 4) 84 break; 85 get_le32(pb); // version 86 if (vst) { 87 vst->codec->codec_tag = get_le32(pb); 88 vst->codec->codec_id = 89 ff_codec_get_id(ff_codec_bmp_tags, vst->codec->codec_tag); 90 if (vst->codec->codec_tag == MKTAG('R', 'J', 'P', 'G')) 91 vst->codec->codec_id = CODEC_ID_NUV; 92 } else 93 url_fskip(pb, 4); 94 95 if (ast) { 96 ast->codec->codec_tag = get_le32(pb); 97 ast->codec->sample_rate = get_le32(pb); 98 ast->codec->bits_per_coded_sample = get_le32(pb); 99 ast->codec->channels = get_le32(pb); 100 ast->codec->codec_id = 101 ff_wav_codec_get_id(ast->codec->codec_tag, 102 ast->codec->bits_per_coded_sample); 103 ast->need_parsing = AVSTREAM_PARSE_FULL; 104 } else 105 url_fskip(pb, 4 * 4); 106 107 size -= 6 * 4; 108 url_fskip(pb, size); 109 return 1; 110 case NUV_SEEKP: 111 size = 11; 112 break; 113 default: 114 url_fskip(pb, 7); 115 size = PKTSIZE(get_le32(pb)); 116 break; 117 } 118 url_fskip(pb, size); 119 } 120 return 0; 121} 122 123static int nuv_header(AVFormatContext *s, AVFormatParameters *ap) { 124 NUVContext *ctx = s->priv_data; 125 ByteIOContext *pb = s->pb; 126 char id_string[12]; 127 double aspect, fps; 128 int is_mythtv, width, height, v_packs, a_packs; 129 int stream_nr = 0; 130 AVStream *vst = NULL, *ast = NULL; 131 get_buffer(pb, id_string, 12); 132 is_mythtv = !memcmp(id_string, "MythTVVideo", 12); 133 url_fskip(pb, 5); // version string 134 url_fskip(pb, 3); // padding 135 width = get_le32(pb); 136 height = get_le32(pb); 137 get_le32(pb); // unused, "desiredwidth" 138 get_le32(pb); // unused, "desiredheight" 139 get_byte(pb); // 'P' == progressive, 'I' == interlaced 140 url_fskip(pb, 3); // padding 141 aspect = av_int2dbl(get_le64(pb)); 142 if (aspect > 0.9999 && aspect < 1.0001) 143 aspect = 4.0 / 3.0; 144 fps = av_int2dbl(get_le64(pb)); 145 146 // number of packets per stream type, -1 means unknown, e.g. streaming 147 v_packs = get_le32(pb); 148 a_packs = get_le32(pb); 149 get_le32(pb); // text 150 151 get_le32(pb); // keyframe distance (?) 152 153 if (v_packs) { 154 ctx->v_id = stream_nr++; 155 vst = av_new_stream(s, ctx->v_id); 156 if (!vst) 157 return AVERROR(ENOMEM); 158 vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; 159 vst->codec->codec_id = CODEC_ID_NUV; 160 vst->codec->width = width; 161 vst->codec->height = height; 162 vst->codec->bits_per_coded_sample = 10; 163 vst->sample_aspect_ratio = av_d2q(aspect * height / width, 10000); 164 vst->r_frame_rate = av_d2q(fps, 60000); 165 av_set_pts_info(vst, 32, 1, 1000); 166 } else 167 ctx->v_id = -1; 168 169 if (a_packs) { 170 ctx->a_id = stream_nr++; 171 ast = av_new_stream(s, ctx->a_id); 172 if (!ast) 173 return AVERROR(ENOMEM); 174 ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; 175 ast->codec->codec_id = CODEC_ID_PCM_S16LE; 176 ast->codec->channels = 2; 177 ast->codec->sample_rate = 44100; 178 ast->codec->bit_rate = 2 * 2 * 44100 * 8; 179 ast->codec->block_align = 2 * 2; 180 ast->codec->bits_per_coded_sample = 16; 181 av_set_pts_info(ast, 32, 1, 1000); 182 } else 183 ctx->a_id = -1; 184 185 get_codec_data(pb, vst, ast, is_mythtv); 186 ctx->rtjpg_video = vst && vst->codec->codec_id == CODEC_ID_NUV; 187 return 0; 188} 189 190#define HDRSIZE 12 191 192static int nuv_packet(AVFormatContext *s, AVPacket *pkt) { 193 NUVContext *ctx = s->priv_data; 194 ByteIOContext *pb = s->pb; 195 uint8_t hdr[HDRSIZE]; 196 nuv_frametype frametype; 197 int ret, size; 198 while (!url_feof(pb)) { 199 int copyhdrsize = ctx->rtjpg_video ? HDRSIZE : 0; 200 uint64_t pos = url_ftell(pb); 201 ret = get_buffer(pb, hdr, HDRSIZE); 202 if (ret < HDRSIZE) 203 return ret < 0 ? ret : AVERROR(EIO); 204 frametype = hdr[0]; 205 size = PKTSIZE(AV_RL32(&hdr[8])); 206 switch (frametype) { 207 case NUV_EXTRADATA: 208 if (!ctx->rtjpg_video) { 209 url_fskip(pb, size); 210 break; 211 } 212 case NUV_VIDEO: 213 if (ctx->v_id < 0) { 214 av_log(s, AV_LOG_ERROR, "Video packet in file without video stream!\n"); 215 url_fskip(pb, size); 216 break; 217 } 218 ret = av_new_packet(pkt, copyhdrsize + size); 219 if (ret < 0) 220 return ret; 221 // HACK: we have no idea if it is a keyframe, 222 // but if we mark none seeking will not work at all. 223 pkt->flags |= AV_PKT_FLAG_KEY; 224 pkt->pos = pos; 225 pkt->pts = AV_RL32(&hdr[4]); 226 pkt->stream_index = ctx->v_id; 227 memcpy(pkt->data, hdr, copyhdrsize); 228 ret = get_buffer(pb, pkt->data + copyhdrsize, size); 229 if (ret < 0) { 230 av_free_packet(pkt); 231 return ret; 232 } 233 if (ret < size) 234 av_shrink_packet(pkt, copyhdrsize + ret); 235 return 0; 236 case NUV_AUDIO: 237 if (ctx->a_id < 0) { 238 av_log(s, AV_LOG_ERROR, "Audio packet in file without audio stream!\n"); 239 url_fskip(pb, size); 240 break; 241 } 242 ret = av_get_packet(pb, pkt, size); 243 pkt->flags |= AV_PKT_FLAG_KEY; 244 pkt->pos = pos; 245 pkt->pts = AV_RL32(&hdr[4]); 246 pkt->stream_index = ctx->a_id; 247 if (ret < 0) return ret; 248 return 0; 249 case NUV_SEEKP: 250 // contains no data, size value is invalid 251 break; 252 default: 253 url_fskip(pb, size); 254 break; 255 } 256 } 257 return AVERROR(EIO); 258} 259 260AVInputFormat nuv_demuxer = { 261 "nuv", 262 NULL_IF_CONFIG_SMALL("NuppelVideo format"), 263 sizeof(NUVContext), 264 nuv_probe, 265 nuv_header, 266 nuv_packet, 267 NULL, 268 NULL, 269 .flags = AVFMT_GENERIC_INDEX, 270}; 271