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 }