# HG changeset patch # User fraserofthenight # Date 1247105799 25200 # Node ID a1202aac1124ae729f6a1ec1af7560086f98e560 # Parent e6cf9f26d0e7edc37f6b929336fbc88bd057c9eb Started implementing separate encoder class diff -r e6cf9f26d0e7 -r a1202aac1124 .settings/descent.core.prefs --- a/.settings/descent.core.prefs Wed Jul 08 07:29:48 2009 -0700 +++ b/.settings/descent.core.prefs Wed Jul 08 19:16:39 2009 -0700 @@ -1,4 +1,4 @@ -#Sun Jun 21 09:02:00 PDT 2009 +#Wed Jul 08 18:44:23 PDT 2009 descent.core.compiler.allowDeprecated=enabled descent.core.compiler.debugIdentifiers=AVBuffer descent.core.compiler.debugLevel=1 @@ -6,8 +6,8 @@ descent.core.compiler.enableWarnings=enabled descent.core.compiler.source=1 descent.core.compiler.taskCaseSensitive=enabled -descent.core.compiler.taskPriorities=NORMAL,LOW,HIGH,HIGH,LOW,HIGH,LOW,LOW,LOW,HIGH -descent.core.compiler.taskTags=TODO,PERHAPS,BUG,WTF,NEXTVERSION,WORKAREA,OPTIMIZE,CBUG,PORT,IMPORTANT +descent.core.compiler.taskPriorities=NORMAL,LOW,HIGH,HIGH,LOW,HIGH,LOW,LOW,LOW,HIGH,NORMAL +descent.core.compiler.taskTags=TODO,PERHAPS,BUG,WTF,NEXTVERSION,WORKAREA,OPTIMIZE,CBUG,PORT,IMPORTANT,TEMP descent.core.compiler.versionIdentifiers=DigitalMars, Windows, Win32, X86, LittleEndian, D_InlineAsm, D_InlineAsm_X86, all, Tango, Descent descent.core.compiler.versionLevel=0 descent.core.formatter.alignment_for_array_literals=0 diff -r e6cf9f26d0e7 -r a1202aac1124 bin/frankenbuild.map --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/frankenbuild.map Wed Jul 08 19:16:39 2009 -0700 @@ -0,0 +1,41 @@ + + Start Length Name Class + 0002:00000000 000213BBH _TEXT CODE 32-bit + 0002:000213BC 000001FEH ICODE ICODE 32-bit + 0003:00000000 00000004H .CRT$XIA DATA 32-bit + 0003:00000010 00000004H .CRT$XIZ DATA 32-bit + 0003:00000020 00000004H .CRT$XCA DATA 32-bit + 0003:00000030 00000004H .CRT$XCZ DATA 32-bit + 0003:00000040 00000004H .CRT$XPA DATA 32-bit + 0003:00000050 00000004H .CRT$XPZ DATA 32-bit + 0003:00000060 00000004H .CRT$XTA DATA 32-bit + 0003:00000070 00000004H .CRT$XTZ DATA 32-bit + 0003:00000074 00000000H IMP__DATA IMP__DATA 32-bit + 0003:00000080 0000E91CH _DATA DATA 32-bit + 0003:0000E99C 00000000H FMB DATA 32-bit + 0003:0000E99C 00000110H FM DATA 32-bit + 0003:0000EAAC 00000000H FME DATA 32-bit + 0003:0000EAAC 00000000H XIB DATA 32-bit + 0003:0000EAAC 00000018H XI DATA 32-bit + 0003:0000EAC4 00000000H XIE DATA 32-bit + 0003:0000EAC4 00000000H XCB DATA 32-bit + 0003:0000EAC4 00000010H XC DATA 32-bit + 0003:0000EAD4 00000000H XCE DATA 32-bit + 0003:0000EAD4 00000000H XIFCB DATA 32-bit + 0003:0000EAD4 00000004H XIFU DATA 32-bit + 0003:0000EAD8 00000000H XIFL DATA 32-bit + 0003:0000EAD8 00000004H XIFM DATA 32-bit + 0003:0000EADC 00000000H XIFCE DATA 32-bit + 0003:0000EAE0 00000000H CONST CONST 32-bit + 0003:0000EAE0 00000000H EEND ENDBSS 32-bit + 0003:0000EAE0 000019FCH _BSS BSS 32-bit + 0003:000104DC 00000000H XOB BSS 32-bit + 0003:000104DC 00000004H XO BSS 32-bit + 0003:000104E0 00000000H XOE BSS 32-bit + 0003:000104E0 00000000H XOFB BSS 32-bit + 0003:000104E0 00000108H XOF BSS 32-bit + 0003:000105E8 00000000H XOFE BSS 32-bit + 0003:000105F0 00000419H c_common BSS 32-bit + 0003:00010A10 00000000H STACK STACK 32-bit + +Program entry point at 0001A090 diff -r e6cf9f26d0e7 -r a1202aac1124 ref/buffer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ref/buffer.d Wed Jul 08 19:16:39 2009 -0700 @@ -0,0 +1,244 @@ +/** + * 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.util.buffer; + +import tango.time.StopWatch; +import tango.core.sync.Condition; +import tango.core.sync.Mutex; + +version(Windows) +{ + import tango.stdc.stringz; + import tango.text.convert.Format; + import tango.sys.win32.UserGdi; + + import hoofbaby.util.exception; + + private extern(Windows) + { + HANDLE CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR); + LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD); + LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap, LPVOID lpBaseAddress); + BOOL UnmapViewOfFile(LPCVOID); + } + + private uint bufNum = 0; + + /** + * Allocates a buffer of the given size. ALl accesses up to buf + (size * 2) + * are valid; those over buf + size will reach around + * + * Params: + * size = The size of the buffer (must be multiple of page size; use large powers of two) + * Returns: A pointer to the beginning of that memory + */ + private void* _allocBuffer(uint size) + { + // Ensure page file is the right size + SYSTEM_INFO info; + GetSystemInfo(&info); + uint pageSize = info.dwAllocationGranularity; + if(size % pageSize) // PERHAPS next multiple of instead of error? + throw new IllegalOperationException(Format("Memory map pagefile mismatch -- " + "requested {0}, but page size is {1}", size, pageSize)); + + // Try to allocate the buffer + HANDLE mmap = CreateFileMappingA(INVALID_HANDLE_VALUE, null, PAGE_READWRITE, 0, size, null); + if(!mmap) throw new UnrecoverableException("Could not create memory mapping for buffer"); + void* buf = MapViewOfFile(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size); + if(!buf) throw new UnrecoverableException("Could not map view of buffer"); + void* buf2 = MapViewOfFileEx(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size, buf + size); + if(!buf2) throw new UnrecoverableException("Could not map second view of buffer"); + if(buf2 != buf + size) throw new UnrecoverableException("Memory mapped buffers are not contiguous"); + return buf; + } + + private void _freeBuffer(void* buffer, uint size) + { + UnmapViewOfFile(buffer); + UnmapViewOfFile(buffer + size); + } +} +else +{ + static assert(false, "Unsupported operating system"); +} + +// IMPORTANT synchronization!!!! +public struct RingBuffer +{ + private const double WAIT_TIME = 0.01; // 10 ms + private static Condition NULL_CONDITION; + + private uint size; + private void* addr; + + private uint readOfs; + private uint writeOfs; + + /** + * The callback function to be called upon an overflow or underflow. + * + * Params: + * buffer = The RIngBuffer instance with the issue + * sizeRequested = the size the operation requested + * sizeAvailable = how much is actually there + * Returns: true if the function should return null, false if the buffer should still + * return the requested pointer (i.e. if the function waited until something + * was available) + */ + public alias bool delegate(RingBuffer* buffer, uint sizeRequested, uint sizeAvialbale) BufferCallback; + public BufferCallback onUnderflow; + public BufferCallback onOverflow; + + debug(AVBuffer) + { + private uint readRequested = 0; + private uint writeRequested = 0; + } + + public static RingBuffer opCall(uint size) + { + // Not really a singleton; who cares if more than one is constructed? + if(!NULL_CONDITION) + NULL_CONDITION = new Condition(new Mutex()); + + RingBuffer r; + assert(size > 0); + r.size = size; + r.addr = _allocBuffer(size); + return r; + } + + public void free() + { + _freeBuffer(addr, size); + addr = null; + } + + public void* beginRead(uint requested) + { + debug(AVBuffer) + { + assert(readRequested == 0); + readRequested = requested; + } + + if(requested > available) + { + if(!onUnderflow || onUnderflow(this, requested, available)) + { + debug(AVBuffer) readRequested = 0; + return null; + } + } + + return addr + readOfs; + } + + public void endRead(uint bytes) + { + debug(AVBuffer) + { + assert(bytes == readRequested); + readRequested = 0; + } + + readOfs += bytes; + if(readOfs > size) + { + readOfs -= bytes; + writeOfs -= bytes; + } + } + + public void* beginWrite(uint requested) + { + debug(AVBuffer) + { + assert(writeRequested == 0); + writeRequested = requested; + } + + if(requested > freeSpace) + { + if(!onOverflow || onOverflow(this, requested, available)) + { + return null; + } + } + + return addr + writeOfs; + } + + public void endWrite(uint bytes) + { + debug(AVBuffer) + { + assert(bytes == writeRequested); + writeRequested = 0; + } + + writeOfs += bytes; + } + + public uint available() + { + return writeOfs - readOfs; + } + + public uint freeSpace() + { + return size - available(); + } + + public uint totalSize() + { + return size; + } + + public void clear() + { + readOfs = 0; + writeOfs = 0; + } + + public void isEmpty() + { + return readOfs == writeOfs; + } + + private bool _waitFor(char[] exp)(uint bytes, uint timeoutMs, Condition cond = null) + { + if(!cond) + cond = NULL_CONDITION; + + ulong microsecs = timeoutMs * 1000; + StopWatch sw; + sw.start(); + do + { + if(mixin(exp) >= bytes) + return true; + if(cond.wait(WAIT_TIME)) + return false; + } while(sw.microsec() < microsecs); + return false; + } + + public alias _waitFor!("available") waitAvailable; + public alias _waitFor!("freeSpace") waitFreeSpace; +} \ No newline at end of file diff -r e6cf9f26d0e7 -r a1202aac1124 src/build/frankenbuild.d --- a/src/build/frankenbuild.d Wed Jul 08 07:29:48 2009 -0700 +++ b/src/build/frankenbuild.d Wed Jul 08 19:16:39 2009 -0700 @@ -64,7 +64,6 @@ if(cats.contains("transcode")) { targetFound = true; - cats ~= "platif-debug"; char[] cargs = cbase.dup; cargs ~= " -g"; cargs ~= " -debug -debug=AVBuffer"; @@ -72,7 +71,6 @@ cargs ~= " -I../src/test"; cargs ~= " -oqobjs/debug"; cargs ~= " ../src/test/hoofbaby/test/adhoc/transcode.d"; - commands ~= { return cpp("../src/platif/platif.h", "../src/impl/hoofbaby/platinum/platif.d"); }; commands ~= { return rebuild(cargs); }; } diff -r e6cf9f26d0e7 -r a1202aac1124 src/impl/hoofbaby/codec/encoder.d --- /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); + } +} diff -r e6cf9f26d0e7 -r a1202aac1124 src/impl/hoofbaby/codec/libav/avbuffer.d --- a/src/impl/hoofbaby/codec/libav/avbuffer.d Wed Jul 08 07:29:48 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - * 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.libav.avbuffer; - -import hoofbaby.codec.libav.avformat; -import hoofbaby.util.buffer; -import tango.stdc.stdlib; -import tango.stdc.string; -import tango.stdc.stdio; - -ByteIOContext* getBioContext(RingBuffer* buf, int maxPacketSize) -{ - ByteIOContext* bio = av_alloc_put_byte(cast(char*) malloc(1 << 16), 1 << 16, 1, buf, null, &hoofbaby_avbuffer_write, null); - if(bio) - bio.is_streamed = true; - return bio; -} - -extern(C) int hoofbaby_avbuffer_write(void* _buf, char* data, int size) -{ - RingBuffer* buf = cast(RingBuffer*) _buf; - assert(buf); - void* p = buf.beginWrite(size); - assert(p); - memcpy(p, data, size); - buf.endWrite(size); - return 0; -} diff -r e6cf9f26d0e7 -r a1202aac1124 src/impl/hoofbaby/codec/libav/avutil.d --- a/src/impl/hoofbaby/codec/libav/avutil.d Wed Jul 08 07:29:48 2009 -0700 +++ b/src/impl/hoofbaby/codec/libav/avutil.d Wed Jul 08 19:16:39 2009 -0700 @@ -83,10 +83,6 @@ PIX_FMT_NB = 60, } -alias char* function(void*) _BCD_func__901; -alias void function(void*, int, char*, char*) _BCD_func__880; -alias int function() _BCD_func__823; - alias int AVRounding; enum : AVRounding { @@ -100,7 +96,7 @@ struct AVClass { char* class_name; - _BCD_func__901 item_name; + char* function(void*) item_name; void* option; } @@ -119,7 +115,7 @@ extern(C) { void av_log_default_callback(void*, int, char*, char*); - void av_log_set_callback(_BCD_func__880); + void av_log_set_callback(void function(void*, int, char*, char*)); void av_log_set_level(int); int av_log_get_level(); void av_vlog(void*, int, char*, char*); diff -r e6cf9f26d0e7 -r a1202aac1124 src/impl/hoofbaby/codec/libav/swscale.d --- a/src/impl/hoofbaby/codec/libav/swscale.d Wed Jul 08 07:29:48 2009 -0700 +++ b/src/impl/hoofbaby/codec/libav/swscale.d Wed Jul 08 19:16:39 2009 -0700 @@ -45,8 +45,6 @@ const double SWS_MAX_REDUCE_CUTOFF = 0.002; typedef void SwsContext; -alias int function() _BCD_func__867; - struct SwsFilter { SwsVector* lumH; diff -r e6cf9f26d0e7 -r a1202aac1124 src/impl/hoofbaby/util/buffer.d --- a/src/impl/hoofbaby/util/buffer.d Wed Jul 08 07:29:48 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,244 +0,0 @@ -/** - * 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.util.buffer; - -import tango.time.StopWatch; -import tango.core.sync.Condition; -import tango.core.sync.Mutex; - -version(Windows) -{ - import tango.stdc.stringz; - import tango.text.convert.Format; - import tango.sys.win32.UserGdi; - - import hoofbaby.util.exception; - - private extern(Windows) - { - HANDLE CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR); - LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD); - LPVOID MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap, LPVOID lpBaseAddress); - BOOL UnmapViewOfFile(LPCVOID); - } - - private uint bufNum = 0; - - /** - * Allocates a buffer of the given size. ALl accesses up to buf + (size * 2) - * are valid; those over buf + size will reach around - * - * Params: - * size = The size of the buffer (must be multiple of page size; use large powers of two) - * Returns: A pointer to the beginning of that memory - */ - private void* _allocBuffer(uint size) - { - // Ensure page file is the right size - SYSTEM_INFO info; - GetSystemInfo(&info); - uint pageSize = info.dwAllocationGranularity; - if(size % pageSize) // PERHAPS next multiple of instead of error? - throw new IllegalOperationException(Format("Memory map pagefile mismatch -- " - "requested {0}, but page size is {1}", size, pageSize)); - - // Try to allocate the buffer - HANDLE mmap = CreateFileMappingA(INVALID_HANDLE_VALUE, null, PAGE_READWRITE, 0, size, null); - if(!mmap) throw new UnrecoverableException("Could not create memory mapping for buffer"); - void* buf = MapViewOfFile(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size); - if(!buf) throw new UnrecoverableException("Could not map view of buffer"); - void* buf2 = MapViewOfFileEx(mmap, FILE_MAP_ALL_ACCESS, 0, 0, size, buf + size); - if(!buf2) throw new UnrecoverableException("Could not map second view of buffer"); - if(buf2 != buf + size) throw new UnrecoverableException("Memory mapped buffers are not contiguous"); - return buf; - } - - private void _freeBuffer(void* buffer, uint size) - { - UnmapViewOfFile(buffer); - UnmapViewOfFile(buffer + size); - } -} -else -{ - static assert(false, "Unsupported operating system"); -} - -// IMPORTANT synchronization!!!! -public struct RingBuffer -{ - private const double WAIT_TIME = 0.01; // 10 ms - private static Condition NULL_CONDITION; - - private uint size; - private void* addr; - - private uint readOfs; - private uint writeOfs; - - /** - * The callback function to be called upon an overflow or underflow. - * - * Params: - * buffer = The RIngBuffer instance with the issue - * sizeRequested = the size the operation requested - * sizeAvailable = how much is actually there - * Returns: true if the function should return null, false if the buffer should still - * return the requested pointer (i.e. if the function waited until something - * was available) - */ - public alias bool delegate(RingBuffer* buffer, uint sizeRequested, uint sizeAvialbale) BufferCallback; - public BufferCallback onUnderflow; - public BufferCallback onOverflow; - - debug(AVBuffer) - { - private uint readRequested = 0; - private uint writeRequested = 0; - } - - public static RingBuffer opCall(uint size) - { - // Not really a singleton; who cares if more than one is constructed? - if(!NULL_CONDITION) - NULL_CONDITION = new Condition(new Mutex()); - - RingBuffer r; - assert(size > 0); - r.size = size; - r.addr = _allocBuffer(size); - return r; - } - - public void free() - { - _freeBuffer(addr, size); - addr = null; - } - - public void* beginRead(uint requested) - { - debug(AVBuffer) - { - assert(readRequested == 0); - readRequested = requested; - } - - if(requested > available) - { - if(!onUnderflow || onUnderflow(this, requested, available)) - { - debug(AVBuffer) readRequested = 0; - return null; - } - } - - return addr + readOfs; - } - - public void endRead(uint bytes) - { - debug(AVBuffer) - { - assert(bytes == readRequested); - readRequested = 0; - } - - readOfs += bytes; - if(readOfs > size) - { - readOfs -= bytes; - writeOfs -= bytes; - } - } - - public void* beginWrite(uint requested) - { - debug(AVBuffer) - { - assert(writeRequested == 0); - writeRequested = requested; - } - - if(requested > freeSpace) - { - if(!onOverflow || onOverflow(this, requested, available)) - { - return null; - } - } - - return addr + writeOfs; - } - - public void endWrite(uint bytes) - { - debug(AVBuffer) - { - assert(bytes == writeRequested); - writeRequested = 0; - } - - writeOfs += bytes; - } - - public uint available() - { - return writeOfs - readOfs; - } - - public uint freeSpace() - { - return size - available(); - } - - public uint totalSize() - { - return size; - } - - public void clear() - { - readOfs = 0; - writeOfs = 0; - } - - public void isEmpty() - { - return readOfs == writeOfs; - } - - private bool _waitFor(char[] exp)(uint bytes, uint timeoutMs, Condition cond = null) - { - if(!cond) - cond = NULL_CONDITION; - - ulong microsecs = timeoutMs * 1000; - StopWatch sw; - sw.start(); - do - { - if(mixin(exp) >= bytes) - return true; - if(cond.wait(WAIT_TIME)) - return false; - } while(sw.microsec() < microsecs); - return false; - } - - public alias _waitFor!("available") waitAvailable; - public alias _waitFor!("freeSpace") waitFreeSpace; -} \ No newline at end of file