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