|
|
|
@ -1,5 +1,5 @@ |
|
|
|
|
/*
|
|
|
|
|
* Argonaut Games ASF demuxer |
|
|
|
|
* Argonaut Games ASF (de)muxer |
|
|
|
|
* |
|
|
|
|
* Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com) |
|
|
|
|
* |
|
|
|
@ -63,6 +63,7 @@ typedef struct ArgoASFDemuxContext { |
|
|
|
|
uint32_t blocks_read; |
|
|
|
|
} ArgoASFDemuxContext; |
|
|
|
|
|
|
|
|
|
#if CONFIG_ARGO_ASF_DEMUXER |
|
|
|
|
static void argo_asf_parse_file_header(ArgoASFFileHeader *hdr, const uint8_t *buf) |
|
|
|
|
{ |
|
|
|
|
hdr->magic = AV_RL32(buf + 0); |
|
|
|
@ -254,3 +255,127 @@ AVInputFormat ff_argo_asf_demuxer = { |
|
|
|
|
.read_header = argo_asf_read_header, |
|
|
|
|
.read_packet = argo_asf_read_packet |
|
|
|
|
}; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#if CONFIG_ARGO_ASF_MUXER |
|
|
|
|
static int argo_asf_write_init(AVFormatContext *s) |
|
|
|
|
{ |
|
|
|
|
const AVCodecParameters *par; |
|
|
|
|
|
|
|
|
|
if (s->nb_streams != 1) { |
|
|
|
|
av_log(s, AV_LOG_ERROR, "ASF files have exactly one stream\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
par = s->streams[0]->codecpar; |
|
|
|
|
|
|
|
|
|
if (par->codec_id != AV_CODEC_ID_ADPCM_ARGO) { |
|
|
|
|
av_log(s, AV_LOG_ERROR, "%s codec not supported\n", |
|
|
|
|
avcodec_get_name(par->codec_id)); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (par->channels > 2) { |
|
|
|
|
av_log(s, AV_LOG_ERROR, "ASF files only support up to 2 channels\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (par->sample_rate > UINT16_MAX) { |
|
|
|
|
av_log(s, AV_LOG_ERROR, "Sample rate too large\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { |
|
|
|
|
av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void argo_asf_write_file_header(const ArgoASFFileHeader *fhdr, AVIOContext *pb) |
|
|
|
|
{ |
|
|
|
|
avio_wl32( pb, fhdr->magic); |
|
|
|
|
avio_wl16( pb, fhdr->version_major); |
|
|
|
|
avio_wl16( pb, fhdr->version_minor); |
|
|
|
|
avio_wl32( pb, fhdr->num_chunks); |
|
|
|
|
avio_wl32( pb, fhdr->chunk_offset); |
|
|
|
|
avio_write(pb, fhdr->name, sizeof(fhdr->name)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void argo_asf_write_chunk_header(const ArgoASFChunkHeader *ckhdr, AVIOContext *pb) |
|
|
|
|
{ |
|
|
|
|
avio_wl32(pb, ckhdr->num_blocks); |
|
|
|
|
avio_wl32(pb, ckhdr->num_samples); |
|
|
|
|
avio_wl32(pb, ckhdr->unk1); |
|
|
|
|
avio_wl16(pb, ckhdr->sample_rate); |
|
|
|
|
avio_wl16(pb, ckhdr->unk2); |
|
|
|
|
avio_wl32(pb, ckhdr->flags); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int argo_asf_write_header(AVFormatContext *s) |
|
|
|
|
{ |
|
|
|
|
const AVCodecParameters *par = s->streams[0]->codecpar; |
|
|
|
|
ArgoASFFileHeader fhdr; |
|
|
|
|
ArgoASFChunkHeader chdr; |
|
|
|
|
|
|
|
|
|
fhdr.magic = ASF_TAG; |
|
|
|
|
fhdr.version_major = 2; |
|
|
|
|
fhdr.version_minor = 1; |
|
|
|
|
fhdr.num_chunks = 1; |
|
|
|
|
fhdr.chunk_offset = ASF_FILE_HEADER_SIZE; |
|
|
|
|
strncpy(fhdr.name, av_basename(s->url), FF_ARRAY_ELEMS(fhdr.name)); |
|
|
|
|
|
|
|
|
|
chdr.num_blocks = 0; |
|
|
|
|
chdr.num_samples = ASF_SAMPLE_COUNT; |
|
|
|
|
chdr.unk1 = 0; |
|
|
|
|
chdr.sample_rate = par->sample_rate; |
|
|
|
|
chdr.unk2 = ~0; |
|
|
|
|
chdr.flags = ASF_CF_BITS_PER_SAMPLE | ASF_CF_ALWAYS1; |
|
|
|
|
|
|
|
|
|
if (par->channels == 2) |
|
|
|
|
chdr.flags |= ASF_CF_STEREO; |
|
|
|
|
|
|
|
|
|
argo_asf_write_file_header(&fhdr, s->pb); |
|
|
|
|
argo_asf_write_chunk_header(&chdr, s->pb); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int argo_asf_write_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
|
{ |
|
|
|
|
if (pkt->size != 17 * s->streams[0]->codecpar->channels) |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
|
|
|
|
|
if (s->streams[0]->nb_frames >= UINT32_MAX) |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
|
|
|
|
|
avio_write(s->pb, pkt->data, pkt->size); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int argo_asf_write_trailer(AVFormatContext *s) |
|
|
|
|
{ |
|
|
|
|
int64_t ret; |
|
|
|
|
|
|
|
|
|
if ((ret = avio_seek(s->pb, ASF_FILE_HEADER_SIZE, SEEK_SET) < 0)) |
|
|
|
|
return ret; |
|
|
|
|
|
|
|
|
|
avio_wl32(s->pb, (uint32_t)s->streams[0]->nb_frames); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AVOutputFormat ff_argo_asf_muxer = { |
|
|
|
|
.name = "argo_asf", |
|
|
|
|
.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"), |
|
|
|
|
/*
|
|
|
|
|
* NB: Can't do this as it conflicts with the actual ASF format. |
|
|
|
|
* .extensions = "asf", |
|
|
|
|
*/ |
|
|
|
|
.audio_codec = AV_CODEC_ID_ADPCM_ARGO, |
|
|
|
|
.video_codec = AV_CODEC_ID_NONE, |
|
|
|
|
.init = argo_asf_write_init, |
|
|
|
|
.write_header = argo_asf_write_header, |
|
|
|
|
.write_packet = argo_asf_write_packet, |
|
|
|
|
.write_trailer = argo_asf_write_trailer |
|
|
|
|
}; |
|
|
|
|
#endif |
|
|
|
|