Mercurial > projects > hoofbaby
comparison src/test/hoofbaby/test/adhoc/transcode.d @ 3:e6cf9f26d0e7
Added a separate "transcode" app for testing transcoding
author | fraserofthenight |
---|---|
date | Wed, 08 Jul 2009 07:29:48 -0700 |
parents | |
children | 270343d824ae |
comparison
equal
deleted
inserted
replaced
2:52278c5dc19c | 3:e6cf9f26d0e7 |
---|---|
1 /** | |
2 * Hoofbaby -- http://www.dsource.org/projects/hoofbaby | |
3 * Copyright (C) 2009 Robert Fraser | |
4 * | |
5 * This program is free software; you can redistribute it andor | |
6 * modify it under the terms of the GNU General Public License | |
7 * as published by the Free Software Foundation; either version 2 | |
8 * of the License, or (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 * GNU General Public License for more details. | |
14 */ | |
15 | |
16 module hoofbaby.test.adhoc.transcode; | |
17 | |
18 //Imports for whole program (just link to them) | |
19 import NONE_1 = tango.stdc.stdarg; // Must be linked in to prevent strange linker errors | |
20 debug import NONE_2 = tango.core.stacktrace.TraceExceptions; | |
21 import NONE_3 = hoofbaby.codec.libav.mingw; | |
22 | |
23 import tango.stdc.stdio; | |
24 import tango.stdc.stringz; | |
25 import Math = tango.math.Math; | |
26 import tango.io.device.File; | |
27 | |
28 import hoofbaby.app.libs; | |
29 import hoofbaby.util.buffer; | |
30 | |
31 import hoofbaby.codec.libav.avutil; | |
32 import hoofbaby.codec.libav.avcodec; | |
33 import hoofbaby.codec.libav.avformat; | |
34 import hoofbaby.codec.libav.avbuffer; | |
35 | |
36 private const int STREAM_FRAME_RATE = 25; | |
37 private const double STREAM_DURATION = 5.0; | |
38 private const int STREAM_NB_FRAMES = (cast(int) (STREAM_DURATION * STREAM_FRAME_RATE)); | |
39 private const int OUTBUF_SIZE = 100000; | |
40 | |
41 int main(char[][] args) | |
42 { | |
43 initLibs(); | |
44 | |
45 int frameCount = 0; | |
46 double audioCount, audioIncr, audioIncr2; | |
47 char* voutbuf, aoutbuf; | |
48 int res; | |
49 | |
50 AVFrame* allocFrame(int pix_fmt, int width, int height) | |
51 { | |
52 AVFrame* picture; | |
53 char* buf; | |
54 int size; | |
55 | |
56 picture = avcodec_alloc_frame(); | |
57 if(!picture) | |
58 return null; | |
59 size = avpicture_get_size(pix_fmt, width, height); | |
60 buf = cast(char*) av_malloc(size); | |
61 if(!buf) | |
62 { | |
63 av_free(picture); | |
64 return null; | |
65 } | |
66 avpicture_fill(cast(AVPicture*) picture, buf, pix_fmt, width, height); | |
67 return picture; | |
68 } | |
69 | |
70 void generatePicture(AVFrame* pict, int width, int height) | |
71 { | |
72 int x, y, i; | |
73 i = frameCount; | |
74 | |
75 /* Y */ | |
76 for(y = 0; y < height; y++) | |
77 { | |
78 for(x = 0; x < width; x++) | |
79 { | |
80 pict.data[0][y * pict.linesize[0] + x] = x + y + i * 3; | |
81 } | |
82 } | |
83 | |
84 /* Cb and Cr */ | |
85 for(y = 0; y < height / 2; y++) | |
86 { | |
87 for(x = 0; x < width / 2; x++) | |
88 { | |
89 pict.data[1][y * pict.linesize[1] + x] = 128 + y + i * 2; | |
90 pict.data[2][y * pict.linesize[2] + x] = 64 + x + i * 5; | |
91 } | |
92 } | |
93 } | |
94 | |
95 int writeVideoFrame(AVFormatContext* ctx, AVStream* stream, AVFrame* picture) | |
96 { | |
97 AVCodecContext* vcodec = stream.codec; | |
98 int ret = 0; | |
99 | |
100 if(frameCount >= STREAM_NB_FRAMES) | |
101 { | |
102 // no more frame to compress. The codec has a latency of a few | |
103 // frames if using B frames, so we get the last frames by | |
104 // passing the same picture again | |
105 } | |
106 else | |
107 { | |
108 generatePicture(picture, vcodec.width, vcodec.height); | |
109 } | |
110 | |
111 // Encode it! | |
112 int outSize = avcodec_encode_video(vcodec, voutbuf, OUTBUF_SIZE, picture); | |
113 // if zero size, it means the image was buffered.. if not, write that ****! | |
114 if(outSize > 0) | |
115 { | |
116 AVPacket pkt; | |
117 av_init_packet(&pkt); | |
118 | |
119 pkt.pts = av_rescale_q(vcodec.coded_frame.pts, vcodec.time_base, stream.time_base); | |
120 if(vcodec.coded_frame.key_frame) | |
121 pkt.flags |= PKT_FLAG_KEY; | |
122 pkt.stream_index = stream.index; | |
123 pkt.data = voutbuf; | |
124 pkt.size = outSize; | |
125 | |
126 // oh yeah! | |
127 ret = av_write_frame(ctx, &pkt); | |
128 } | |
129 else | |
130 { | |
131 ret = 0; | |
132 } | |
133 | |
134 frameCount++; | |
135 assert(!ret, "Error writing video frame"); | |
136 return ret; | |
137 } | |
138 | |
139 int writeAudioFrame(AVFormatContext* ctx, AVStream* stream, short* samples) | |
140 { | |
141 AVCodecContext* acodec = stream.codec; | |
142 | |
143 { | |
144 int j, i, v; | |
145 short *q; | |
146 | |
147 q = samples; | |
148 for(j = 0; j < acodec.frame_size; j++) | |
149 { | |
150 v = cast(int)(Math.sin(audioCount) * 10000); | |
151 for(i = 0; i < 2; i++) // 2 is number of channels | |
152 *q++ = v; | |
153 audioCount += audioIncr; | |
154 audioIncr += audioIncr2; | |
155 } | |
156 } | |
157 | |
158 AVPacket pkt; | |
159 av_init_packet(&pkt); | |
160 pkt.size = avcodec_encode_audio(acodec, aoutbuf, OUTBUF_SIZE, samples); | |
161 //pkt.pts = av_rescale_q(acodec.coded_frame.pts, acodec.time_base, acodec.time_base); | |
162 pkt.flags |= PKT_FLAG_KEY; | |
163 pkt.stream_index = stream.index; | |
164 pkt.data = aoutbuf; | |
165 | |
166 int res = av_write_frame(ctx, &pkt) != 0; | |
167 assert(res == 0, "Error writing audio frame"); | |
168 return res; | |
169 } | |
170 | |
171 //-------------------------------------------------------------------------- | |
172 // Container format | |
173 | |
174 AVOutputFormat* fmt = guess_format("asf", null, null); | |
175 assert(fmt !is null, "Could not find format"); | |
176 | |
177 AVFormatContext* ctx = av_alloc_format_context(); | |
178 assert(ctx !is null, "Could not allocate format context"); | |
179 scope(exit) if(ctx) av_free(ctx); | |
180 ctx.oformat = fmt; | |
181 //ctx.preload = cast(int) (0.5 * AV_TIME_BASE); | |
182 ctx.max_delay = cast(int) (0.7 * AV_TIME_BASE); | |
183 ctx.loop_output = AVFMT_NOOUTPUTLOOP; | |
184 ctx.flags |= AVFMT_FLAG_NONBLOCK; | |
185 | |
186 AVFormatParameters params; | |
187 params.prealloced_context = 1; | |
188 params.video_codec_id = CODEC_ID_WMV2; | |
189 params.audio_codec_id = CODEC_ID_WMAV2; | |
190 params.width = 352; | |
191 params.height = 288; | |
192 params.time_base.num = 1; | |
193 params.time_base.den = STREAM_FRAME_RATE; | |
194 params.pix_fmt = PIX_FMT_YUV420P; | |
195 params.channels = 2; | |
196 params.sample_rate = 44100; | |
197 res = av_set_parameters(ctx, null); | |
198 assert(res >= 0, "Could not set parameters"); | |
199 | |
200 //-------------------------------------------------------------------------- | |
201 // Video stream | |
202 | |
203 AVStream* vstream = av_new_stream(ctx, 0); | |
204 assert(vstream !is null, "Could not allocate video stream"); | |
205 ctx.streams[0] = vstream; | |
206 | |
207 AVCodec* vcodecName = avcodec_find_encoder(CODEC_ID_WMV2); | |
208 assert(vcodecName, "Could not find video codec"); | |
209 AVCodecContext* vcodec = vstream.codec; | |
210 vcodec.codec_id = CODEC_ID_WMV2; | |
211 vcodec.codec_type = CODEC_TYPE_VIDEO; | |
212 vcodec.bit_rate = 400000; | |
213 vcodec.width = 352; | |
214 vcodec.height = 288; | |
215 vcodec.gop_size = 12; | |
216 vcodec.qmin = 3; | |
217 vcodec.time_base.den = STREAM_FRAME_RATE; | |
218 vcodec.time_base.num = 1; | |
219 vcodec.pix_fmt = PIX_FMT_YUV420P; | |
220 vcodec.flags |= CODEC_FLAG_GLOBAL_HEADER; | |
221 res = avcodec_open(vcodec, vcodecName); | |
222 assert(res >= 0, "Could not open video codec"); | |
223 | |
224 //-------------------------------------------------------------------------- | |
225 // Audio stream | |
226 | |
227 AVStream* astream = av_new_stream(ctx, 0); | |
228 assert(astream !is null, "Could not allocate audio stream"); | |
229 ctx.streams[1] = astream; | |
230 | |
231 AVCodec* acodecName = avcodec_find_encoder(CODEC_ID_WMAV2); | |
232 assert(acodecName, "Could not find audio codec"); | |
233 AVCodecContext* acodec = astream.codec; | |
234 acodec.codec_id = CODEC_ID_WMAV2; | |
235 acodec.codec_type = CODEC_TYPE_AUDIO; | |
236 acodec.bit_rate = 64000; | |
237 acodec.sample_rate = 44100; | |
238 acodec.channels = 2; | |
239 acodec.flags |= CODEC_FLAG_GLOBAL_HEADER; | |
240 audioCount = 0.0; | |
241 audioIncr = 2 * Math.PI * 110.0 / acodec.sample_rate; | |
242 audioIncr2 = 2 * Math.PI * 110.0 / acodec.sample_rate / acodec.sample_rate; | |
243 res = avcodec_open(acodec, acodecName); | |
244 assert(res >= 0, "Could not open audio codec"); | |
245 | |
246 //-------------------------------------------------------------------------- | |
247 // Actually doing stuff | |
248 | |
249 // Open output file | |
250 RingBuffer ringBuf = RingBuffer(1 << 24); // 16 MB | |
251 scope(exit) ringBuf.free(); | |
252 ctx.pb = getBioContext(&ringBuf, 1 << 24); | |
253 assert(ctx.pb !is null); | |
254 assert(ctx.pb.opaque !is null); | |
255 | |
256 // Allocate a video frame and audio buffer to store stuff | |
257 AVFrame* frame = allocFrame(PIX_FMT_YUV420P, vcodec.width, vcodec.height); | |
258 assert(frame !is null, "Could not allocate frame"); | |
259 scope(exit) if(frame) av_free(frame); | |
260 short* samples = cast(short*) av_malloc(acodec.frame_size * 2 * acodec.channels); | |
261 assert(frame !is null, "Could not allocate samples"); | |
262 scope(exit) if(samples) av_free(samples); | |
263 | |
264 // Allocate some output buffers | |
265 voutbuf = cast(char*) av_malloc(OUTBUF_SIZE); | |
266 assert(voutbuf !is null, "Could not allocate video output buffer"); | |
267 scope(exit) if(voutbuf) av_free(voutbuf); | |
268 aoutbuf = cast(char*) av_malloc(OUTBUF_SIZE); | |
269 assert(aoutbuf !is null, "Could not allocate audio output buffer"); | |
270 scope(exit) if(aoutbuf) av_free(voutbuf); | |
271 | |
272 // Write the header | |
273 res = av_write_header(ctx); | |
274 assert(res >= 0, "Could not write header for output file (incorrect codec paramters?)"); | |
275 | |
276 while(true) | |
277 { | |
278 double audio_pts = cast(double) astream.pts.val * astream.time_base.num / astream.time_base.den; | |
279 double video_pts = cast(double) vstream.pts.val * vstream.time_base.num / vstream.time_base.den; | |
280 | |
281 if(audio_pts >= STREAM_DURATION && video_pts >= STREAM_DURATION) | |
282 break; | |
283 | |
284 // Write interleaved audio & video | |
285 if(audio_pts < video_pts) | |
286 writeAudioFrame(ctx, astream, samples); | |
287 else | |
288 writeVideoFrame(ctx, vstream, frame); | |
289 } | |
290 | |
291 res = av_write_trailer(ctx); | |
292 assert(res >= 0, "Could not write trailer for output file"); | |
293 | |
294 scope File file = new File("biff_happy.wmv", File.WriteCreate); | |
295 uint available = ringBuf.available; | |
296 auto addr = ringBuf.beginRead(available); | |
297 file.write(addr[0 .. available]); | |
298 ringBuf.endRead(available); | |
299 file.close(); | |
300 | |
301 return 0; | |
302 } |