FFmpeg/libavformat/lxfdec.c
Anton Khirnov 9200514ad8 lavf: replace AVStream.codec with AVStream.codecpar
Currently, AVStream contains an embedded AVCodecContext instance, which
is used by demuxers to export stream parameters to the caller and by
muxers to receive stream parameters from the caller. It is also used
internally as the codec context that is passed to parsers.

In addition, it is also widely used by the callers as the decoding (when
demuxer) or encoding (when muxing) context, though this has been
officially discouraged since Libav 11.

There are multiple important problems with this approach:
    - the fields in AVCodecContext are in general one of
        * stream parameters
        * codec options
        * codec state
      However, it's not clear which ones are which. It is consequently
      unclear which fields are a demuxer allowed to set or a muxer allowed to
      read. This leads to erratic behaviour depending on whether decoding or
      encoding is being performed or not (and whether it uses the AVStream
      embedded codec context).
    - various synchronization issues arising from the fact that the same
      context is used by several different APIs (muxers/demuxers,
      parsers, bitstream filters and encoders/decoders) simultaneously, with
      there being no clear rules for who can modify what and the different
      processes being typically delayed with respect to each other.
    - avformat_find_stream_info() making it necessary to support opening
      and closing a single codec context multiple times, thus
      complicating the semantics of freeing various allocated objects in the
      codec context.

Those problems are resolved by replacing the AVStream embedded codec
context with a newly added AVCodecParameters instance, which stores only
the stream parameters exported by the demuxers or read by the muxers.
2016-02-23 17:01:58 +01:00

344 lines
11 KiB
C

/*
* LXF demuxer
* Copyright (c) 2010 Tomas Härdin
*
* This file is part of Libav.
*
* Libav is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Libav is distributed in the hope that it will be useful,
* 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
* License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <inttypes.h>
#include "libavutil/intreadwrite.h"
#include "libavcodec/bytestream.h"
#include "avformat.h"
#include "internal.h"
#define LXF_MAX_PACKET_HEADER_SIZE 256
#define LXF_HEADER_DATA_SIZE 120
#define LXF_IDENT "LEITCH\0"
#define LXF_IDENT_LENGTH 8
#define LXF_SAMPLERATE 48000
static const AVCodecTag lxf_tags[] = {
{ AV_CODEC_ID_MJPEG, 0 },
{ AV_CODEC_ID_MPEG1VIDEO, 1 },
{ AV_CODEC_ID_MPEG2VIDEO, 2 }, //MpMl, 4:2:0
{ AV_CODEC_ID_MPEG2VIDEO, 3 }, //MpPl, 4:2:2
{ AV_CODEC_ID_DVVIDEO, 4 }, //DV25
{ AV_CODEC_ID_DVVIDEO, 5 }, //DVCPRO
{ AV_CODEC_ID_DVVIDEO, 6 }, //DVCPRO50
{ AV_CODEC_ID_RAWVIDEO, 7 }, //AV_PIX_FMT_ARGB, where alpha is used for chroma keying
{ AV_CODEC_ID_RAWVIDEO, 8 }, //16-bit chroma key
{ AV_CODEC_ID_MPEG2VIDEO, 9 }, //4:2:2 CBP ("Constrained Bytes per Gop")
{ AV_CODEC_ID_NONE, 0 },
};
typedef struct LXFDemuxContext {
int channels; ///< number of audio channels. zero means no audio
int frame_number; ///< current video frame
uint32_t video_format, packet_type, extended_size;
} LXFDemuxContext;
static int lxf_probe(AVProbeData *p)
{
if (!memcmp(p->buf, LXF_IDENT, LXF_IDENT_LENGTH))
return AVPROBE_SCORE_MAX;
return 0;
}
/**
* Verify the checksum of an LXF packet header
*
* @param[in] header the packet header to check
* @return zero if the checksum is OK, non-zero otherwise
*/
static int check_checksum(const uint8_t *header, int size)
{
int x;
uint32_t sum = 0;
for (x = 0; x < size; x += 4)
sum += AV_RL32(&header[x]);
return sum;
}
/**
* Read input until we find the next ident. If found, copy it to the header buffer
*
* @param[out] header where to copy the ident to
* @return 0 if an ident was found, < 0 on I/O error
*/
static int sync(AVFormatContext *s, uint8_t *header)
{
uint8_t buf[LXF_IDENT_LENGTH];
int ret;
if ((ret = avio_read(s->pb, buf, LXF_IDENT_LENGTH)) != LXF_IDENT_LENGTH)
return ret < 0 ? ret : AVERROR_EOF;
while (memcmp(buf, LXF_IDENT, LXF_IDENT_LENGTH)) {
if (s->pb->eof_reached)
return AVERROR_EOF;
memmove(buf, &buf[1], LXF_IDENT_LENGTH-1);
buf[LXF_IDENT_LENGTH-1] = avio_r8(s->pb);
}
memcpy(header, LXF_IDENT, LXF_IDENT_LENGTH);
return 0;
}
/**
* Read and checksum the next packet header
*
* @return the size of the payload following the header or < 0 on failure
*/
static int get_packet_header(AVFormatContext *s)
{
LXFDemuxContext *lxf = s->priv_data;
AVIOContext *pb = s->pb;
int track_size, samples, ret;
uint32_t version, audio_format, header_size, channels, tmp;
AVStream *st;
uint8_t header[LXF_MAX_PACKET_HEADER_SIZE];
const uint8_t *p = header + LXF_IDENT_LENGTH;
//find and read the ident
if ((ret = sync(s, header)) < 0)
return ret;
ret = avio_read(pb, header + LXF_IDENT_LENGTH, 8);
if (ret != 8)
return ret < 0 ? ret : AVERROR_EOF;
version = bytestream_get_le32(&p);
header_size = bytestream_get_le32(&p);
if (version > 1)
avpriv_request_sample(s, "Format version %"PRIu32, version);
if (header_size < (version ? 72 : 60) ||
header_size > LXF_MAX_PACKET_HEADER_SIZE ||
(header_size & 3)) {
av_log(s, AV_LOG_ERROR, "Invalid header size 0x%"PRIx32"\n", header_size);
return AVERROR_INVALIDDATA;
}
//read the rest of the packet header
ret = avio_read(pb, header + (p - header), header_size - (p - header));
if (ret != header_size - (p - header))
return ret < 0 ? ret : AVERROR_EOF;
if (check_checksum(header, header_size))
av_log(s, AV_LOG_ERROR, "checksum error\n");
lxf->packet_type = bytestream_get_le32(&p);
p += version ? 20 : 12;
lxf->extended_size = 0;
switch (lxf->packet_type) {
case 0:
//video
lxf->video_format = bytestream_get_le32(&p);
ret = bytestream_get_le32(&p);
//skip VBI data and metadata
avio_skip(pb, (int64_t)(uint32_t)AV_RL32(p + 4) +
(int64_t)(uint32_t)AV_RL32(p + 12));
break;
case 1:
//audio
if (s->nb_streams < 2) {
av_log(s, AV_LOG_INFO, "got audio packet, but no audio stream present\n");
break;
}
if (version == 0)
p += 8;
audio_format = bytestream_get_le32(&p);
channels = bytestream_get_le32(&p);
track_size = bytestream_get_le32(&p);
st = s->streams[1];
//set codec based on specified audio bitdepth
//we only support tightly packed 16-, 20-, 24- and 32-bit PCM at the moment
st->codecpar->bits_per_coded_sample = (audio_format >> 6) & 0x3F;
if (st->codecpar->bits_per_coded_sample != (audio_format & 0x3F)) {
av_log(s, AV_LOG_WARNING, "only tightly packed PCM currently supported\n");
return AVERROR_PATCHWELCOME;
}
switch (st->codecpar->bits_per_coded_sample) {
case 16: st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE_PLANAR; break;
case 20: st->codecpar->codec_id = AV_CODEC_ID_PCM_LXF; break;
case 24: st->codecpar->codec_id = AV_CODEC_ID_PCM_S24LE_PLANAR; break;
case 32: st->codecpar->codec_id = AV_CODEC_ID_PCM_S32LE_PLANAR; break;
default:
av_log(s, AV_LOG_WARNING,
"only 16-, 20-, 24- and 32-bit PCM currently supported\n");
return AVERROR_PATCHWELCOME;
}
samples = track_size * 8 / st->codecpar->bits_per_coded_sample;
//use audio packet size to determine video standard
//for NTSC we have one 8008-sample audio frame per five video frames
if (samples == LXF_SAMPLERATE * 5005 / 30000) {
avpriv_set_pts_info(s->streams[0], 64, 1001, 30000);
} else {
//assume PAL, but warn if we don't have 1920 samples
if (samples != LXF_SAMPLERATE / 25)
av_log(s, AV_LOG_WARNING,
"video doesn't seem to be PAL or NTSC. guessing PAL\n");
avpriv_set_pts_info(s->streams[0], 64, 1, 25);
}
//TODO: warning if track mask != (1 << channels) - 1?
ret = av_popcount(channels) * track_size;
break;
default:
tmp = bytestream_get_le32(&p);
ret = bytestream_get_le32(&p);
if (tmp == 1)
lxf->extended_size = bytestream_get_le32(&p);
break;
}
return ret;
}
static int lxf_read_header(AVFormatContext *s)
{
LXFDemuxContext *lxf = s->priv_data;
AVIOContext *pb = s->pb;
uint8_t header_data[LXF_HEADER_DATA_SIZE];
int ret;
AVStream *st;
uint32_t video_params, disk_params;
uint16_t record_date, expiration_date;
if ((ret = get_packet_header(s)) < 0)
return ret;
if (ret != LXF_HEADER_DATA_SIZE) {
av_log(s, AV_LOG_ERROR, "expected %d B size header, got %d\n",
LXF_HEADER_DATA_SIZE, ret);
return AVERROR_INVALIDDATA;
}
if ((ret = avio_read(pb, header_data, LXF_HEADER_DATA_SIZE)) != LXF_HEADER_DATA_SIZE)
return ret < 0 ? ret : AVERROR_EOF;
if (!(st = avformat_new_stream(s, NULL)))
return AVERROR(ENOMEM);
st->duration = AV_RL32(&header_data[32]);
video_params = AV_RL32(&header_data[40]);
record_date = AV_RL16(&header_data[56]);
expiration_date = AV_RL16(&header_data[58]);
disk_params = AV_RL32(&header_data[116]);
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->bit_rate = 1000000 * ((video_params >> 14) & 0xFF);
st->codecpar->codec_tag = video_params & 0xF;
st->codecpar->codec_id = ff_codec_get_id(lxf_tags, st->codecpar->codec_tag);
av_log(s, AV_LOG_DEBUG, "record: %x = %i-%02i-%02i\n",
record_date, 1900 + (record_date & 0x7F), (record_date >> 7) & 0xF,
(record_date >> 11) & 0x1F);
av_log(s, AV_LOG_DEBUG, "expire: %x = %i-%02i-%02i\n",
expiration_date, 1900 + (expiration_date & 0x7F), (expiration_date >> 7) & 0xF,
(expiration_date >> 11) & 0x1F);
if ((video_params >> 22) & 1)
av_log(s, AV_LOG_WARNING, "VBI data not yet supported\n");
if ((lxf->channels = 1 << (disk_params >> 4 & 3) + 1)) {
if (!(st = avformat_new_stream(s, NULL)))
return AVERROR(ENOMEM);
st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
st->codecpar->sample_rate = LXF_SAMPLERATE;
st->codecpar->channels = lxf->channels;
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
}
avio_skip(s->pb, lxf->extended_size);
return 0;
}
static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
LXFDemuxContext *lxf = s->priv_data;
AVIOContext *pb = s->pb;
uint32_t stream;
int ret, ret2;
if ((ret = get_packet_header(s)) < 0)
return ret;
stream = lxf->packet_type;
if (stream > 1) {
av_log(s, AV_LOG_WARNING,
"got packet with illegal stream index %"PRIu32"\n", stream);
return AVERROR(EAGAIN);
}
if (stream == 1 && s->nb_streams < 2) {
av_log(s, AV_LOG_ERROR, "got audio packet without having an audio stream\n");
return AVERROR_INVALIDDATA;
}
if ((ret2 = av_new_packet(pkt, ret)) < 0)
return ret2;
if ((ret2 = avio_read(pb, pkt->data, ret)) != ret) {
av_packet_unref(pkt);
return ret2 < 0 ? ret2 : AVERROR_EOF;
}
pkt->stream_index = stream;
if (!stream) {
//picture type (0 = closed I, 1 = open I, 2 = P, 3 = B)
if (((lxf->video_format >> 22) & 0x3) < 2)
pkt->flags |= AV_PKT_FLAG_KEY;
pkt->dts = lxf->frame_number++;
}
return ret;
}
AVInputFormat ff_lxf_demuxer = {
.name = "lxf",
.long_name = NULL_IF_CONFIG_SMALL("VR native stream (LXF)"),
.priv_data_size = sizeof(LXFDemuxContext),
.read_probe = lxf_probe,
.read_header = lxf_read_header,
.read_packet = lxf_read_packet,
.codec_tag = (const AVCodecTag* const []){lxf_tags, 0},
};