blob: 321b113032e9fb531d550ce96fcb09dc2a816fc6
1 | /* |
2 | * Creative Voice File muxer. |
3 | * Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org> |
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 "voc.h" |
23 | #include "internal.h" |
24 | |
25 | |
26 | typedef struct voc_enc_context { |
27 | int param_written; |
28 | } VocEncContext; |
29 | |
30 | static int voc_write_header(AVFormatContext *s) |
31 | { |
32 | AVIOContext *pb = s->pb; |
33 | AVCodecParameters *par = s->streams[0]->codecpar; |
34 | const int header_size = 26; |
35 | const int version = 0x0114; |
36 | |
37 | if (s->nb_streams != 1 |
38 | || s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) |
39 | return AVERROR_PATCHWELCOME; |
40 | |
41 | if (!par->codec_tag && par->codec_id != AV_CODEC_ID_PCM_U8) { |
42 | av_log(s, AV_LOG_ERROR, "unsupported codec\n"); |
43 | return AVERROR(EINVAL); |
44 | } |
45 | |
46 | avio_write(pb, ff_voc_magic, sizeof(ff_voc_magic) - 1); |
47 | avio_wl16(pb, header_size); |
48 | avio_wl16(pb, version); |
49 | avio_wl16(pb, ~version + 0x1234); |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static int voc_write_packet(AVFormatContext *s, AVPacket *pkt) |
55 | { |
56 | VocEncContext *voc = s->priv_data; |
57 | AVCodecParameters *par = s->streams[0]->codecpar; |
58 | AVIOContext *pb = s->pb; |
59 | |
60 | if (!voc->param_written) { |
61 | if (par->codec_tag > 3) { |
62 | avio_w8(pb, VOC_TYPE_NEW_VOICE_DATA); |
63 | avio_wl24(pb, pkt->size + 12); |
64 | avio_wl32(pb, par->sample_rate); |
65 | avio_w8(pb, par->bits_per_coded_sample); |
66 | avio_w8(pb, par->channels); |
67 | avio_wl16(pb, par->codec_tag); |
68 | avio_wl32(pb, 0); |
69 | } else { |
70 | if (s->streams[0]->codecpar->channels > 1) { |
71 | avio_w8(pb, VOC_TYPE_EXTENDED); |
72 | avio_wl24(pb, 4); |
73 | avio_wl16(pb, 65536-(256000000 + par->sample_rate*par->channels/2)/(par->sample_rate*par->channels)); |
74 | avio_w8(pb, par->codec_tag); |
75 | avio_w8(pb, par->channels - 1); |
76 | } |
77 | avio_w8(pb, VOC_TYPE_VOICE_DATA); |
78 | avio_wl24(pb, pkt->size + 2); |
79 | avio_w8(pb, 256 - (1000000 + par->sample_rate/2) / par->sample_rate); |
80 | avio_w8(pb, par->codec_tag); |
81 | } |
82 | voc->param_written = 1; |
83 | } else { |
84 | avio_w8(pb, VOC_TYPE_VOICE_DATA_CONT); |
85 | avio_wl24(pb, pkt->size); |
86 | } |
87 | |
88 | avio_write(pb, pkt->data, pkt->size); |
89 | return 0; |
90 | } |
91 | |
92 | static int voc_write_trailer(AVFormatContext *s) |
93 | { |
94 | avio_w8(s->pb, 0); |
95 | return 0; |
96 | } |
97 | |
98 | AVOutputFormat ff_voc_muxer = { |
99 | .name = "voc", |
100 | .long_name = NULL_IF_CONFIG_SMALL("Creative Voice"), |
101 | .mime_type = "audio/x-voc", |
102 | .extensions = "voc", |
103 | .priv_data_size = sizeof(VocEncContext), |
104 | .audio_codec = AV_CODEC_ID_PCM_S16LE, |
105 | .video_codec = AV_CODEC_ID_NONE, |
106 | .write_header = voc_write_header, |
107 | .write_packet = voc_write_packet, |
108 | .write_trailer = voc_write_trailer, |
109 | .codec_tag = (const AVCodecTag* const []){ ff_voc_codec_tags, 0 }, |
110 | .flags = AVFMT_NOTIMESTAMPS, |
111 | }; |
112 |