mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2024-12-24 00:02:52 +00:00
9200514ad8
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.
344 lines
11 KiB
C
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},
|
|
};
|