1/* 2 * VPK demuxer 3 * Copyright (c) 2015 Paul B Mahol 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/intreadwrite.h" 23#include "avformat.h" 24#include "demux.h" 25#include "internal.h" 26 27typedef struct VPKDemuxContext { 28 unsigned data_start; 29 unsigned block_count; 30 unsigned current_block; 31 unsigned last_block_size; 32} VPKDemuxContext; 33 34static int vpk_probe(const AVProbeData *p) 35{ 36 if (AV_RL32(p->buf) != MKBETAG('V','P','K',' ')) 37 return 0; 38 39 return AVPROBE_SCORE_MAX / 3 * 2; 40} 41 42static int vpk_read_header(AVFormatContext *s) 43{ 44 VPKDemuxContext *vpk = s->priv_data; 45 unsigned offset; 46 unsigned samples_per_block; 47 AVStream *st; 48 49 vpk->current_block = 0; 50 st = avformat_new_stream(s, NULL); 51 if (!st) 52 return AVERROR(ENOMEM); 53 54 avio_skip(s->pb, 4); 55 st->duration = avio_rl32(s->pb) * 28 / 16; 56 offset = avio_rl32(s->pb); 57 st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 58 st->codecpar->codec_id = AV_CODEC_ID_ADPCM_PSX; 59 st->codecpar->block_align = avio_rl32(s->pb); 60 st->codecpar->sample_rate = avio_rl32(s->pb); 61 if (st->codecpar->sample_rate <= 0 || st->codecpar->block_align <= 0) 62 return AVERROR_INVALIDDATA; 63 st->codecpar->ch_layout.nb_channels = avio_rl32(s->pb); 64 if (st->codecpar->ch_layout.nb_channels <= 0) 65 return AVERROR_INVALIDDATA; 66 samples_per_block = ((st->codecpar->block_align / st->codecpar->ch_layout.nb_channels) * 28LL) / 16; 67 if (samples_per_block <= 0) 68 return AVERROR_INVALIDDATA; 69 vpk->block_count = (st->duration + (samples_per_block - 1)) / samples_per_block; 70 vpk->last_block_size = (st->duration % samples_per_block) * 16 * st->codecpar->ch_layout.nb_channels / 28; 71 72 if (offset < avio_tell(s->pb)) 73 return AVERROR_INVALIDDATA; 74 avio_skip(s->pb, offset - avio_tell(s->pb)); 75 vpk->data_start = offset; 76 avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); 77 78 return 0; 79} 80 81static int vpk_read_packet(AVFormatContext *s, AVPacket *pkt) 82{ 83 AVCodecParameters *par = s->streams[0]->codecpar; 84 VPKDemuxContext *vpk = s->priv_data; 85 int ret, i; 86 87 vpk->current_block++; 88 if (vpk->current_block == vpk->block_count) { 89 unsigned size = vpk->last_block_size / par->ch_layout.nb_channels; 90 unsigned skip = (par->block_align - vpk->last_block_size) / par->ch_layout.nb_channels; 91 uint64_t pos = avio_tell(s->pb); 92 93 ret = av_new_packet(pkt, vpk->last_block_size); 94 if (ret < 0) 95 return ret; 96 for (i = 0; i < par->ch_layout.nb_channels; i++) { 97 ret = avio_read(s->pb, pkt->data + i * size, size); 98 avio_skip(s->pb, skip); 99 if (ret != size) { 100 return AVERROR(EIO); 101 } 102 } 103 pkt->pos = pos; 104 pkt->stream_index = 0; 105 } else if (vpk->current_block < vpk->block_count) { 106 ret = av_get_packet(s->pb, pkt, par->block_align); 107 pkt->stream_index = 0; 108 } else { 109 return AVERROR_EOF; 110 } 111 112 return ret; 113} 114 115static int vpk_read_seek(AVFormatContext *s, int stream_index, 116 int64_t timestamp, int flags) 117{ 118 AVStream *st = s->streams[stream_index]; 119 AVCodecParameters *par = st->codecpar; 120 VPKDemuxContext *vpk = s->priv_data; 121 int samples_per_block; 122 int64_t ret = 0; 123 124 samples_per_block = av_get_audio_frame_duration2(par, par->block_align); 125 if (samples_per_block > 0) 126 timestamp /= samples_per_block; 127 else 128 return -1; 129 ret = avio_seek(s->pb, vpk->data_start + timestamp * par->block_align, SEEK_SET); 130 if (ret < 0) 131 return ret; 132 133 vpk->current_block = timestamp; 134 avpriv_update_cur_dts(s, st, timestamp * samples_per_block); 135 return 0; 136} 137 138const AVInputFormat ff_vpk_demuxer = { 139 .name = "vpk", 140 .long_name = NULL_IF_CONFIG_SMALL("Sony PS2 VPK"), 141 .priv_data_size = sizeof(VPKDemuxContext), 142 .read_probe = vpk_probe, 143 .read_header = vpk_read_header, 144 .read_packet = vpk_read_packet, 145 .read_seek = vpk_read_seek, 146 .extensions = "vpk", 147}; 148