LCOV - code coverage report
Current view: top level - libavformat - nuv.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 144 226 63.7 %
Date: 2017-12-18 20:14:19 Functions: 4 6 66.7 %

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

Generated by: LCOV version 1.13