diff src/impl/hoofbaby/codec/encoder.d @ 4:a1202aac1124

Started implementing separate encoder class
author fraserofthenight
date Wed, 08 Jul 2009 19:16:39 -0700
parents
children 270343d824ae
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/impl/hoofbaby/codec/encoder.d	Wed Jul 08 19:16:39 2009 -0700
@@ -0,0 +1,187 @@
+/**
+ * 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.codec.encoder;
+
+import hoofbaby.codec.libav.avutil;
+import hoofbaby.codec.libav.avcodec;
+import hoofbaby.codec.libav.avformat;
+
+public final class Encoder
+{
+	// TODO convert asserts to exceptions
+	
+	private const int OUTBUF_SIZE = 100000;
+	
+	private AVOutputFormat* format;
+	private AVFormatContext* formatContext;
+	
+	private AVCodec* audioCodec;
+	private AVCodecContext* audioContext;
+	private AVStream* audioStream;
+	private char* audioOutbuf;
+	
+	private AVCodec* videoCodec;
+	private AVCodecContext* videoContext;
+	private AVStream* videoStream;
+	private char* videoOutbuf;
+	
+	public this()
+	{
+		int ret; // Stores return value of functions called within
+		
+		//--------------------------------------------------------------------------
+		// Container format
+		
+		format = guess_format("asf", null, null);
+		assert(format !is null, "Could not find format");
+		
+		formatContext = av_alloc_format_context();
+		assert(formatContext !is null, "Could not allocate format context");
+		res.add(formatContext, &av_free);
+		formatContext.oformat = fmt;
+		//ctx.preload = cast(int) (0.5 * AV_TIME_BASE);
+		formatContext.max_delay = cast(int) (0.7 * AV_TIME_BASE);
+		formatContext.loop_output = AVFMT_NOOUTPUTLOOP;
+		formatContext.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; // TEMP
+		params.height = 288; // TEMP
+		params.time_base.num = 1; // TEMP
+		params.time_base.den = 25; // TEMP
+		params.pix_fmt = PIX_FMT_YUV420P;
+		params.channels = 2; // NEXTVERSION support >2 channels for devices that can handle it
+		params.sample_rate = 44100; // TEMP
+		ret = av_set_parameters(formatContext, null);
+		assert(ret >= 0, "Could not set parameters");
+		
+		//--------------------------------------------------------------------------
+		// Video stream
+		
+		videoStream = av_new_stream(formatContext, 0);
+		assert(videoStream !is null, "Could not allocate video stream");
+		formatContext.streams[0] = vstream;
+		
+		videoCodec = avcodec_find_encoder(CODEC_ID_WMV2);
+		assert(videoCodec !is null, "Could not find video codec");
+		
+		videoContext = videoStream.codec;
+		videoContext.codec_id = CODEC_ID_WMV2;
+		videoContext.codec_type = CODEC_TYPE_VIDEO;
+		videoContext.bit_rate = 400000; //TEMP
+		videoContext.width = 352; // TEMP
+		videoContext.height = 288; // TEMP
+		videoContext.gop_size = 12; // TEMP
+		videoContext.qmin = 3; // TEMP
+		videoContext.time_base.den = 25; // TEMP
+		videoContext.time_base.num = 1; // TEMP
+		videoContext.pix_fmt = PIX_FMT_YUV420P;
+		videoContext.flags |= CODEC_FLAG_GLOBAL_HEADER;
+		ret = avcodec_open(videoContext, videoCodec);
+		assert(ret >= 0, "Could not open video codec");
+		
+		videoOutbuf = cast(char*) av_malloc(OUTBUF_SIZE);
+		assert(videoOutbuf !is null, "Could not allocate video output buffer");
+		
+		//--------------------------------------------------------------------------
+		// Audio stream
+		
+		audioStream = av_new_stream(formatContext, 0);
+		assert(audioStream !is null, "Could not allocate audio stream");
+		formatContext.streams[1] = audioStream;
+		
+		audioCodec = avcodec_find_encoder(CODEC_ID_WMAV2);
+		assert(audioCodec !is null, "Could not find audio codec");
+		
+		audioContext = audioStream.codec;
+		audioContext.codec_id = CODEC_ID_WMAV2;
+		audioContext.codec_type = CODEC_TYPE_AUDIO;
+		audioContext.bit_rate = 64000;
+		audioContext.sample_rate = 44100;
+		audioContext.channels = 2;
+		audioContext.flags |= CODEC_FLAG_GLOBAL_HEADER;
+		ret = avcodec_open(audioContext, audioCodec);
+		assert(res >= 0, "Could not open audio codec");
+		
+		audioOutbuf = cast(char*) av_malloc(OUTBUF_SIZE);
+		assert(audioOutbuf !is null, "Could not allocate audio output buffer");
+		
+		scope(failure)
+		{
+			freeResources();
+		}
+	}
+	
+	public ~this()
+	{
+		freeResources();
+	}
+	
+	public void writeHeader()
+	{
+		int ret = av_write_header(formatContext);
+		assert(ret >= 0, "Could not write header for output file (incorrect codec paramters?)");
+	}
+	
+	public void writeTrailer()
+	{
+		int ret = av_write_trailer(ctx);
+		assert(ret >= 0, "Could not write trailer for output file");
+	}
+	
+	public void writeVideoFrame(AVFrame* frame)
+	{
+		int outSize = avcodec_encode_video(videoCodec, videoOutbuf, OUTBUF_SIZE, frame);
+		
+		// if zero size, it means the image was buffered, so don't write the packet
+		if(outSize > 0)
+		{
+			AVPacket pkt;
+			av_init_packet(&pkt);
+			pkt.pts = av_rescale_q(videoCodec.coded_frame.pts, videoCodec.time_base, videoStream.time_base);
+			if(videoCodec.coded_frame.key_frame)
+				pkt.flags |= PKT_FLAG_KEY;
+			pkt.stream_index = videoStream.index;
+			pkt.data = videoOutbuf;
+			pkt.size = outSize;
+			int ret = av_write_frame(ctx, &pkt);
+			assert(!ret, "Error writing video frame");
+		}
+	}
+	
+	public void writeAudioFrame(short* samples)
+	{
+		AVPacket pkt;
+		av_init_packet(&pkt);
+		pkt.size = avcodec_encode_audio(audioCodec, audioOutbuf, 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 = audioStream.index;
+		pkt.data = audioOutbuf;
+		int ret = av_write_frame(ctx, &pkt) != 0;
+		assert(ret == 0, "Error writing audio frame");
+	}
+	
+	private void freeResources()
+	{
+		if(formatContext) av_free(formatContext);
+		if(audioOutbuf)   av_free(audioOutbuf);
+		if(videoOutbuf)   av_free(videoOutbuf);
+	}
+}