module Gui; import gtk.Main; import gtk.MainWindow; import gtk.Alignment; import gtk.Button; import gtk.ToggleButton; import gtk.CheckButton; import gtk.RadioButton; import gtk.ComboBox; import gtk.SpinButton; import gtk.Label; import gtk.Frame; import gtk.HBox; import gtk.VBox; import gtk.Table; import gtk.Widget; import gtkc.gtktypes; import glib.ListSG; import tango.math.Math; import tango.io.Stdout; import BitrateCalculator; void main(char[][] args) { Main.init(args); new BitrateWindow(); Main.run(); } public class BitrateWindow : MainWindow { VideoFrame videoFrame; AudioFrame audioFrame1; AudioFrame audioFrame2; ComboBox codecCombo; ComboBox containCombo; RadioButton fileRadio; SpinButton sizeKBSpin; SpinButton sizeMBSpin; ComboBox mediumCombo; RadioButton bitrRadio; SpinButton bitrSpin; SpinButton videoSizeKBSpin; SpinButton videoSizeMBSpin; bool handlingEvents; this() { super("Bitrate Calculator"); Initialize(); CreateLayout(); SetDefaults(); SetEvents(); showAll(); } void Initialize() { //Video videoFrame = new VideoFrame; //Audio audioFrame1 = new AudioFrame("Audio 1"); audioFrame2 = new AudioFrame("Audio 2"); //Codec codecCombo = new ComboBox(true); foreach(char[] string; videoCodecs) codecCombo.appendText(string); //Container containCombo = new ComboBox(true); foreach(char[] string; containerTypes) containCombo.appendText(string); //Size fileRadio = new RadioButton(cast(ListSG)null, "File Size"); sizeKBSpin = new SpinButton(0, int.max, 1); sizeMBSpin = new SpinButton(0, int.max, 1); mediumCombo = new ComboBox(true); foreach(char[] string; storageMediums) mediumCombo.appendText(string); //Results bitrRadio = new RadioButton(fileRadio, "Average Bitrate"); bitrSpin = new SpinButton(0, int.max, 1); videoSizeKBSpin = new SpinButton(0, int.max, 1); videoSizeMBSpin = new SpinButton(0, int.max, 1); } void CreateLayout() { //Audio HBox audioBox = new HBox(false, 4); audioBox.add(audioFrame1); audioBox.add(audioFrame2); //Codec Frame codecFrame = new Frame("Codec"); Alignment codecAlign = new Alignment(0, 0.5, 0.2, 0); codecAlign.setPadding(4, 4, 4, 4); codecAlign.add(codecCombo); codecFrame.add(codecAlign); //Container Frame containFrame = new Frame("Container"); Alignment containAlign = new Alignment(0, 0.5, 0.2, 0); containAlign.setPadding(4, 4, 4, 4); containAlign.add(containCombo); containFrame.add(containAlign); //Size Frame sizeFrame = new Frame("Total size"); Table sizeTable = new Table(3, 3, false); sizeTable.setRowSpacings(5); sizeTable.setColSpacings(5); Alignment sizeAlign = new Alignment(0, 0.5, 0.2, 0); sizeAlign.setPadding(4, 4, 4, 4); sizeFrame.add(sizeAlign); sizeTable.attachDefaults(fileRadio, 0, 1, 0, 1); sizeTable.attachDefaults(sizeKBSpin, 1, 2, 0, 1); sizeTable.attachDefaults(new Label("KB"), 2, 3, 0, 1); sizeTable.attachDefaults(sizeMBSpin, 1, 2, 1, 2); sizeTable.attachDefaults(new Label("MB"), 2, 3, 1, 2); sizeTable.attachDefaults(new Label("Storage medium"), 0, 1, 2, 3); sizeTable.attachDefaults(mediumCombo, 1, 2, 2, 3); sizeAlign.add(sizeTable); //Results Frame resultFrame = new Frame("Results"); Table resultTable = new Table(3, 3, false); resultTable.setRowSpacings(5); resultTable.setColSpacings(5); Alignment resultAlign = new Alignment(0, 0.5, 0.2, 0); resultAlign.setPadding(4, 4, 4, 4); resultFrame.add(resultAlign); Alignment fileSizeAlign = new Alignment(1, 0.5, 0, 0); fileSizeAlign.add(new Label("Video file size")); resultTable.attachDefaults(bitrRadio, 0, 1, 0, 1); resultTable.attachDefaults(bitrSpin, 1, 2, 0, 1); resultTable.attachDefaults(new Label("kbit/s"), 2, 3, 0, 1); resultTable.attachDefaults(fileSizeAlign, 0, 1, 1, 2); resultTable.attachDefaults(videoSizeKBSpin, 1, 2, 1, 2); resultTable.attachDefaults(new Label("KB"), 2, 3, 1, 2); resultTable.attachDefaults(videoSizeMBSpin, 1, 2, 2, 3); resultTable.attachDefaults(new Label("MB"), 2, 3, 2, 3); resultAlign.add(resultTable); //Left VBox leftBox = new VBox(false, 4); leftBox.add(videoFrame); leftBox.add(audioBox); //Right VBox rightBox = new VBox(false, 4); Alignment closeAlign = new Alignment(1, 1, 0.1, 0); closeAlign.add(new Button(StockID.CLOSE, &OnClose)); rightBox.packStart(codecFrame, false, false, 0); rightBox.packStart(containFrame, false, false, 0); rightBox.packStart(sizeFrame, false, false, 0); rightBox.packStart(resultFrame, false, false, 0); rightBox.add(closeAlign); //top HBox topBox = new HBox(false, 6); topBox.setBorderWidth(4); topBox.add(leftBox); topBox.add(rightBox); this.add(topBox); } void SetDefaults() { handlingEvents = true; codecCombo.setActive(1); containCombo.setActive(0); sizeKBSpin.setValue(storageMediumSizeKB[8]); sizeMBSpin.setValue(rndint(cast(double)storageMediumSizeKB[8] / 1024)); mediumCombo.setActive(8); handlingEvents = false; } void SetEvents() { videoFrame.addOnFramesChanged( &onFramesChanged); videoFrame.addOnBFramesChanged(&onBFramesChanged); audioFrame1.addOnAudioChanged(&onAudioChanged); audioFrame2.addOnAudioChanged(&onAudioChanged); codecCombo.addOnChanged( &OnCodecContainerChange); containCombo.addOnChanged( &OnCodecContainerChange); sizeKBSpin.addOnValueChanged( &OnSizeChange); sizeMBSpin.addOnValueChanged( &OnSizeChange); mediumCombo.addOnChanged( &OnMediumChange); bitrSpin.addOnValueChanged( &OnBitrateChange); videoSizeKBSpin.addOnValueChanged( &OnVideoSizeChange); videoSizeMBSpin.addOnValueChanged( &OnVideoSizeChange); } public void onFramesChanged() { audioFrame1.setLengthInSeconds(videoFrame.getTotalLengthInSeconds); audioFrame2.setLengthInSeconds(videoFrame.getTotalLengthInSeconds); Calculate(); } public void onBFramesChanged() { Calculate(); } public void onAudioChanged() { Calculate(); } public void OnCodecContainerChange(ComboBox cmb) { Calculate(); } public void OnSizeChange(SpinButton spin) { if (handlingEvents) return; handlingEvents = true; fileRadio.setActive(true); if(spin is sizeKBSpin) sizeMBSpin.setValue(rndint(sizeKBSpin.getValue() / 1024)); else sizeKBSpin.setValue(sizeMBSpin.getValueAsInt() * 1024); Calculate(); handlingEvents = false; } public void OnMediumChange(ComboBox cmb) { if (handlingEvents) return; handlingEvents = true; fileRadio.setActive(true); sizeKBSpin.setValue(storageMediumSizeKB[mediumCombo.getActive()]); sizeMBSpin.setValue(rndint(cast(double)storageMediumSizeKB[mediumCombo.getActive()] / 1024)); Calculate(); handlingEvents = false; } public void OnBitrateChange(SpinButton spin) { if (handlingEvents) return; handlingEvents = true; bitrRadio.setActive(true); Calculate(); handlingEvents = false; } public void OnVideoSizeChange(SpinButton spin) { if (handlingEvents) return; handlingEvents = true; bitrRadio.setActive(true); Stdout(handlingEvents).newline; if(spin is videoSizeMBSpin) videoSizeMBSpin.setValue(rndint(videoSizeKBSpin.getValue() / 1024)); else videoSizeKBSpin.setValue(videoSizeMBSpin.getValueAsInt() * 1024); Stdout(handlingEvents).newline; Calculate(); handlingEvents = false; } public void OnClose(Button btn) { Main.exit(0); } void Calculate() { handlingEvents = true; BitrateCalculator bitr = new BitrateCalculator(); VideoCodec codec = cast(VideoCodec)codecCombo.getActive(); bool useBframes = videoFrame.getUseBFrames(); ContainerType container = cast(ContainerType)containCombo.getActive(); AudioStream [] audioStreams; int nbOfFrames = videoFrame.getFrames(); double framerate = videoFrame.getFrameRate(); int videoSizeKB; if(audioFrame1.getAudioStream.SizeBytes > 0) audioStreams ~= audioFrame1.getAudioStream; if(audioFrame2.getAudioStream.SizeBytes > 0) audioStreams ~= audioFrame2.getAudioStream; if(bitrRadio.getActive() == true) // calculate FileSize { int desiredBitrate = bitrSpin.getValueAsInt(); int sizeKB = bitr.CalculateFileSizeKB(codec, useBframes, container, audioStreams, desiredBitrate, nbOfFrames, framerate, videoSizeKB); sizeKBSpin.setValue(sizeKB); sizeMBSpin.setValue(rndint(cast(double)sizeKB / 1024)); } else // calculate Bitrate { long desiredOutputSizeBytes = sizeKBSpin.getValueAsInt() * 1024L; bitrSpin.setValue(bitr.CalculateBitrateKBits(codec, useBframes, container, audioStreams, desiredOutputSizeBytes, nbOfFrames, framerate, videoSizeKB)); } videoSizeKBSpin.setValue(videoSizeKB); videoSizeMBSpin.setValue(rndint(cast(double)videoSizeKB / 1024)); handlingEvents = false; } } class VideoFrame : Frame { void delegate()[] onFramesChangedDelegates; void delegate()[] onBFramesChangedDelegates; SpinButton hourSpin; SpinButton minSpin; SpinButton secSpin; SpinButton totalSecSpin; ComboBox frameRateCombo; SpinButton framesSpin; CheckButton bFramesCheckBtn; bool handlingEvents; public this(char[] title = "Video") { super(title); hourSpin = new SpinButton(0, 40, 1); hourSpin.setWidthChars(3); minSpin = new SpinButton(0, 59, 1); minSpin.setWidthChars(3); secSpin = new SpinButton(0, 59, 1); secSpin.setWidthChars(3); HBox timeBox = new HBox(false, 4); timeBox.add(new Label("Hours")); timeBox.add(hourSpin); timeBox.add(new Label("Minutes")); timeBox.add(minSpin); timeBox.add(new Label("Seconds")); timeBox.add(secSpin); totalSecSpin = new SpinButton(0, 363599, 1); frameRateCombo = new ComboBox(true); foreach(char[] string; frameRates) frameRateCombo.appendText(string); framesSpin = new SpinButton(0, 9089975, 1); bFramesCheckBtn = new CheckButton("B-frames"); Table videoTable = new Table(4, 2, false); videoTable.setRowSpacings(5); videoTable.attachDefaults(Alignment.west(new Label("Total lenght in seconds")), 0, 1, 0, 1); videoTable.attachDefaults(totalSecSpin, 1, 2, 0, 1); videoTable.attachDefaults(Alignment.west(new Label("Framerate")), 0, 1, 1, 2); videoTable.attachDefaults(frameRateCombo, 1, 2, 1, 2); videoTable.attachDefaults(Alignment.west(new Label("Number of frames")), 0, 1, 2, 3); videoTable.attachDefaults(framesSpin, 1, 2, 2, 3); videoTable.attachDefaults(bFramesCheckBtn, 0, 2, 3, 4); VBox videoBox = new VBox(false, 4); videoBox.setBorderWidth(4); videoBox.add(timeBox); videoBox.add(videoTable); this.add(videoBox); hourSpin.addOnValueChanged( &onTimeChanged); minSpin.addOnValueChanged( &onTimeChanged); secSpin.addOnValueChanged( &onTimeChanged); totalSecSpin.addOnValueChanged( &onSecondsChanged); frameRateCombo.addOnChanged( &onFrameRateChanged); framesSpin.addOnValueChanged( &onFramesChanged); bFramesCheckBtn.addOnToggled( &onBFramesChanged); hourSpin.grabFocus(); frameRateCombo.setActive(2); bFramesCheckBtn.setActive(true); } int getTotalLengthInSeconds() { return totalSecSpin.getValueAsInt(); } double getFrameRate() { return FrameRate[frameRateCombo.getActive()]; } int getFrames() { return framesSpin.getValueAsInt(); } bool getUseBFrames() { return cast(bool)bFramesCheckBtn.getActive(); } void addOnFramesChanged(void delegate() dlg) { onFramesChangedDelegates ~= dlg; } void framesChanged() { foreach(dlg; onFramesChangedDelegates) dlg(); } void addOnBFramesChanged(void delegate() dlg) { onBFramesChangedDelegates ~= dlg; } void bFramesChanged() { foreach(dlg; onBFramesChangedDelegates) dlg(); } void onTimeChanged(SpinButton sb) { if (handlingEvents) return; handlingEvents = true; int seconds = hourSpin.getValueAsInt * 3600; seconds += minSpin.getValueAsInt * 60; seconds += secSpin.getValueAsInt; totalSecSpin.setValue(seconds); framesSpin.setValue(cast(int)(seconds * FrameRate[frameRateCombo.getActive()])); framesChanged(); handlingEvents = false; } void onSecondsChanged(SpinButton sb) { if (handlingEvents) return; handlingEvents = true; CalculateTime(); framesSpin.setValue(cast(int)(totalSecSpin.getValue() * FrameRate[frameRateCombo.getActive()])); framesChanged(); handlingEvents = false; } void onFrameRateChanged(ComboBox cb) { framesSpin.setValue(rndint(totalSecSpin.getValue() * FrameRate[frameRateCombo.getActive()])); } void onFramesChanged(SpinButton sb) { if (handlingEvents) return; handlingEvents = true; framesChanged(); totalSecSpin.setValue(rndint(framesSpin.getValue() / FrameRate[frameRateCombo.getActive()])); CalculateTime(); handlingEvents = false; } void onBFramesChanged(ToggleButton tb) { bFramesChanged(); } public void CalculateTime() { int seconds = totalSecSpin.getValueAsInt(); hourSpin.setValue(cast(int)floor(seconds / 3600)); seconds -= hourSpin.getValueAsInt() * 3600; minSpin.setValue(cast(int)floor(seconds / 60)); seconds -= minSpin.getValueAsInt() * 60; secSpin.setValue(seconds); } } class AudioFrame : Frame { void delegate()[] onAudioChangedDelegates; SpinButton bitrSpin; SpinButton sizeKBSpin; SpinButton sizeMBSpin; ComboBox typeCombo; bool handlingEvents; int totalLengthInSeconds; AudioStream audioStream; public this(char[] title = "Audio") { super(title); bitrSpin = new SpinButton(0, 1000, 1); sizeKBSpin = new SpinButton(0, int.max, 1); sizeMBSpin = new SpinButton(0, int.max, 1); typeCombo = new ComboBox(true); foreach(char[] string; audioTypes) typeCombo.appendText(string); Alignment clearAlign = new Alignment(1, 0.5, 0.2, 0); clearAlign.add(new Button(StockID.CLEAR, &OnClearAudio)); HBox kbBox = new HBox(false, 0); kbBox.add(sizeKBSpin); kbBox.add(new Label("KB")); HBox mbBox = new HBox(false, 0); mbBox.add(sizeMBSpin); mbBox.add(new Label("MB")); HBox typeBox = new HBox(false, 0); typeBox.add(new Label("Type")); typeBox.add(typeCombo); VBox audioBox = new VBox(false, 4); audioBox.setBorderWidth(4); audioBox.add(new Label("Bitrate")); audioBox.add(bitrSpin); audioBox.add(new Label("Size")); audioBox.add(kbBox); audioBox.add(mbBox); audioBox.add(typeBox); audioBox.add(clearAlign); this.add(audioBox); bitrSpin.addOnValueChanged( &OnAudioBitrateChange); sizeKBSpin.addOnValueChanged( &OnAudioSizeChange); sizeMBSpin.addOnValueChanged( &OnAudioSizeChange); typeCombo.addOnChanged( &OnAudioTypeChange); typeCombo.setActive(0); } void setLengthInSeconds(int length) { totalLengthInSeconds = length; CalculateAudioSize(); } AudioStream getAudioStream() { return audioStream; } void addOnAudioChanged(void delegate() dlg) { onAudioChangedDelegates ~= dlg; } void audioChanged() { foreach(dlg; onAudioChangedDelegates) dlg(); } public void OnAudioBitrateChange(SpinButton spin) { if (handlingEvents) return; handlingEvents = true; CalculateAudioSize(); audioChanged(); handlingEvents = false; } public void OnAudioSizeChange(SpinButton spin) { if (handlingEvents) return; handlingEvents = true; if(spin is sizeMBSpin) { sizeKBSpin.setValue(sizeMBSpin.getValueAsInt() * 1024); audioStream.SizeBytes = sizeKBSpin.getValueAsInt() * 1024; } else if(spin is sizeKBSpin) { sizeMBSpin.setValue(rndint(sizeKBSpin.getValue() / 1024)); audioStream.SizeBytes = sizeKBSpin.getValueAsInt() * 1024; } audioChanged(); CalculateAudioBitrate(); handlingEvents = false; } public void OnAudioTypeChange(ComboBox cmb) { audioStream.Type = cast(AudioType)typeCombo.getActive(); audioChanged(); } public void OnClearAudio(Button btn) { bitrSpin.setValue(0); sizeKBSpin.setValue(0); sizeMBSpin.setValue(0); typeCombo.setActive(0); audioStream = AudioStream.init; audioChanged(); } void CalculateAudioSize() { double CalculateSize(int bitrate) { double bytesPerSecond = cast(double)bitrate * 1000 / 8; return (totalLengthInSeconds * bytesPerSecond); return 100; } double sizeInBytes = CalculateSize(bitrSpin.getValueAsInt()); audioStream.SizeBytes = rndlong(sizeInBytes); sizeKBSpin.setValue(rndint(sizeInBytes / 1024)); sizeMBSpin.setValue(rndint(sizeInBytes / 1024 / 1024)); } void CalculateAudioBitrate() { int CalculateBitrate(long sizeInBytes) { double bytesPerSecond = sizeInBytes / totalLengthInSeconds; return cast(int)floor(bytesPerSecond * 8 / 1000); } bitrSpin.setValue(CalculateBitrate(audioStream.SizeBytes)); } }