Mercurial > projects > hoofbaby
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/test/hoofbaby/test/adhoc/transcode.d Wed Jul 08 07:29:48 2009 -0700 @@ -0,0 +1,302 @@ +/** + * Hoofbaby -- http://www.dsource.org/projects/hoofbaby + * Copyright (C) 2009 Robert Fraser + * + * This program is free software; you can redistribute it andor + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +module hoofbaby.test.adhoc.transcode; + +//Imports for whole program (just link to them) +import NONE_1 = tango.stdc.stdarg; // Must be linked in to prevent strange linker errors +debug import NONE_2 = tango.core.stacktrace.TraceExceptions; +import NONE_3 = hoofbaby.codec.libav.mingw; + +import tango.stdc.stdio; +import tango.stdc.stringz; +import Math = tango.math.Math; +import tango.io.device.File; + +import hoofbaby.app.libs; +import hoofbaby.util.buffer; + +import hoofbaby.codec.libav.avutil; +import hoofbaby.codec.libav.avcodec; +import hoofbaby.codec.libav.avformat; +import hoofbaby.codec.libav.avbuffer; + +private const int STREAM_FRAME_RATE = 25; +private const double STREAM_DURATION = 5.0; +private const int STREAM_NB_FRAMES = (cast(int) (STREAM_DURATION * STREAM_FRAME_RATE)); +private const int OUTBUF_SIZE = 100000; + +int main(char[][] args) +{ + initLibs(); + + int frameCount = 0; + double audioCount, audioIncr, audioIncr2; + char* voutbuf, aoutbuf; + int res; + + AVFrame* allocFrame(int pix_fmt, int width, int height) + { + AVFrame* picture; + char* buf; + int size; + + picture = avcodec_alloc_frame(); + if(!picture) + return null; + size = avpicture_get_size(pix_fmt, width, height); + buf = cast(char*) av_malloc(size); + if(!buf) + { + av_free(picture); + return null; + } + avpicture_fill(cast(AVPicture*) picture, buf, pix_fmt, width, height); + return picture; + } + + void generatePicture(AVFrame* pict, int width, int height) + { + int x, y, i; + i = frameCount; + + /* Y */ + for(y = 0; y < height; y++) + { + for(x = 0; x < width; x++) + { + pict.data[0][y * pict.linesize[0] + x] = x + y + i * 3; + } + } + + /* Cb and Cr */ + for(y = 0; y < height / 2; y++) + { + for(x = 0; x < width / 2; x++) + { + pict.data[1][y * pict.linesize[1] + x] = 128 + y + i * 2; + pict.data[2][y * pict.linesize[2] + x] = 64 + x + i * 5; + } + } + } + + int writeVideoFrame(AVFormatContext* ctx, AVStream* stream, AVFrame* picture) + { + AVCodecContext* vcodec = stream.codec; + int ret = 0; + + if(frameCount >= STREAM_NB_FRAMES) + { + // no more frame to compress. The codec has a latency of a few + // frames if using B frames, so we get the last frames by + // passing the same picture again + } + else + { + generatePicture(picture, vcodec.width, vcodec.height); + } + + // Encode it! + int outSize = avcodec_encode_video(vcodec, voutbuf, OUTBUF_SIZE, picture); + // if zero size, it means the image was buffered.. if not, write that ****! + if(outSize > 0) + { + AVPacket pkt; + av_init_packet(&pkt); + + pkt.pts = av_rescale_q(vcodec.coded_frame.pts, vcodec.time_base, stream.time_base); + if(vcodec.coded_frame.key_frame) + pkt.flags |= PKT_FLAG_KEY; + pkt.stream_index = stream.index; + pkt.data = voutbuf; + pkt.size = outSize; + + // oh yeah! + ret = av_write_frame(ctx, &pkt); + } + else + { + ret = 0; + } + + frameCount++; + assert(!ret, "Error writing video frame"); + return ret; + } + + int writeAudioFrame(AVFormatContext* ctx, AVStream* stream, short* samples) + { + AVCodecContext* acodec = stream.codec; + + { + int j, i, v; + short *q; + + q = samples; + for(j = 0; j < acodec.frame_size; j++) + { + v = cast(int)(Math.sin(audioCount) * 10000); + for(i = 0; i < 2; i++) // 2 is number of channels + *q++ = v; + audioCount += audioIncr; + audioIncr += audioIncr2; + } + } + + AVPacket pkt; + av_init_packet(&pkt); + pkt.size = avcodec_encode_audio(acodec, aoutbuf, OUTBUF_SIZE, samples); + //pkt.pts = av_rescale_q(acodec.coded_frame.pts, acodec.time_base, acodec.time_base); + pkt.flags |= PKT_FLAG_KEY; + pkt.stream_index = stream.index; + pkt.data = aoutbuf; + + int res = av_write_frame(ctx, &pkt) != 0; + assert(res == 0, "Error writing audio frame"); + return res; + } + + //-------------------------------------------------------------------------- + // Container format + + AVOutputFormat* fmt = guess_format("asf", null, null); + assert(fmt !is null, "Could not find format"); + + AVFormatContext* ctx = av_alloc_format_context(); + assert(ctx !is null, "Could not allocate format context"); + scope(exit) if(ctx) av_free(ctx); + ctx.oformat = fmt; + //ctx.preload = cast(int) (0.5 * AV_TIME_BASE); + ctx.max_delay = cast(int) (0.7 * AV_TIME_BASE); + ctx.loop_output = AVFMT_NOOUTPUTLOOP; + ctx.flags |= AVFMT_FLAG_NONBLOCK; + + AVFormatParameters params; + params.prealloced_context = 1; + params.video_codec_id = CODEC_ID_WMV2; + params.audio_codec_id = CODEC_ID_WMAV2; + params.width = 352; + params.height = 288; + params.time_base.num = 1; + params.time_base.den = STREAM_FRAME_RATE; + params.pix_fmt = PIX_FMT_YUV420P; + params.channels = 2; + params.sample_rate = 44100; + res = av_set_parameters(ctx, null); + assert(res >= 0, "Could not set parameters"); + + //-------------------------------------------------------------------------- + // Video stream + + AVStream* vstream = av_new_stream(ctx, 0); + assert(vstream !is null, "Could not allocate video stream"); + ctx.streams[0] = vstream; + + AVCodec* vcodecName = avcodec_find_encoder(CODEC_ID_WMV2); + assert(vcodecName, "Could not find video codec"); + AVCodecContext* vcodec = vstream.codec; + vcodec.codec_id = CODEC_ID_WMV2; + vcodec.codec_type = CODEC_TYPE_VIDEO; + vcodec.bit_rate = 400000; + vcodec.width = 352; + vcodec.height = 288; + vcodec.gop_size = 12; + vcodec.qmin = 3; + vcodec.time_base.den = STREAM_FRAME_RATE; + vcodec.time_base.num = 1; + vcodec.pix_fmt = PIX_FMT_YUV420P; + vcodec.flags |= CODEC_FLAG_GLOBAL_HEADER; + res = avcodec_open(vcodec, vcodecName); + assert(res >= 0, "Could not open video codec"); + + //-------------------------------------------------------------------------- + // Audio stream + + AVStream* astream = av_new_stream(ctx, 0); + assert(astream !is null, "Could not allocate audio stream"); + ctx.streams[1] = astream; + + AVCodec* acodecName = avcodec_find_encoder(CODEC_ID_WMAV2); + assert(acodecName, "Could not find audio codec"); + AVCodecContext* acodec = astream.codec; + acodec.codec_id = CODEC_ID_WMAV2; + acodec.codec_type = CODEC_TYPE_AUDIO; + acodec.bit_rate = 64000; + acodec.sample_rate = 44100; + acodec.channels = 2; + acodec.flags |= CODEC_FLAG_GLOBAL_HEADER; + audioCount = 0.0; + audioIncr = 2 * Math.PI * 110.0 / acodec.sample_rate; + audioIncr2 = 2 * Math.PI * 110.0 / acodec.sample_rate / acodec.sample_rate; + res = avcodec_open(acodec, acodecName); + assert(res >= 0, "Could not open audio codec"); + + //-------------------------------------------------------------------------- + // Actually doing stuff + + // Open output file + RingBuffer ringBuf = RingBuffer(1 << 24); // 16 MB + scope(exit) ringBuf.free(); + ctx.pb = getBioContext(&ringBuf, 1 << 24); + assert(ctx.pb !is null); + assert(ctx.pb.opaque !is null); + + // Allocate a video frame and audio buffer to store stuff + AVFrame* frame = allocFrame(PIX_FMT_YUV420P, vcodec.width, vcodec.height); + assert(frame !is null, "Could not allocate frame"); + scope(exit) if(frame) av_free(frame); + short* samples = cast(short*) av_malloc(acodec.frame_size * 2 * acodec.channels); + assert(frame !is null, "Could not allocate samples"); + scope(exit) if(samples) av_free(samples); + + // Allocate some output buffers + voutbuf = cast(char*) av_malloc(OUTBUF_SIZE); + assert(voutbuf !is null, "Could not allocate video output buffer"); + scope(exit) if(voutbuf) av_free(voutbuf); + aoutbuf = cast(char*) av_malloc(OUTBUF_SIZE); + assert(aoutbuf !is null, "Could not allocate audio output buffer"); + scope(exit) if(aoutbuf) av_free(voutbuf); + + // Write the header + res = av_write_header(ctx); + assert(res >= 0, "Could not write header for output file (incorrect codec paramters?)"); + + while(true) + { + double audio_pts = cast(double) astream.pts.val * astream.time_base.num / astream.time_base.den; + double video_pts = cast(double) vstream.pts.val * vstream.time_base.num / vstream.time_base.den; + + if(audio_pts >= STREAM_DURATION && video_pts >= STREAM_DURATION) + break; + + // Write interleaved audio & video + if(audio_pts < video_pts) + writeAudioFrame(ctx, astream, samples); + else + writeVideoFrame(ctx, vstream, frame); + } + + res = av_write_trailer(ctx); + assert(res >= 0, "Could not write trailer for output file"); + + scope File file = new File("biff_happy.wmv", File.WriteCreate); + uint available = ringBuf.available; + auto addr = ringBuf.beginRead(available); + file.write(addr[0 .. available]); + ringBuf.endRead(available); + file.close(); + + return 0; +} \ No newline at end of file