VideoPrefsVC.mm 10.8 KB
Newer Older
1
/*
2
 *  Copyright (C) 2015-2016 Savoir-faire Linux Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */
19 20
#import "VideoPrefsVC.h"

21 22 23 24 25 26 27 28 29 30 31 32
#import <QuartzCore/QuartzCore.h>

#import <QItemSelectionModel>
#import <QAbstractProxyModel>

#import <video/configurationproxy.h>
#import <video/sourcemodel.h>
#import <video/previewmanager.h>
#import <video/renderer.h>
#import <video/device.h>
#import <video/devicemodel.h>

33 34
@interface VideoPrefsVC ()

35 36 37 38
@property (assign) IBOutlet NSView* previewView;
@property (assign) IBOutlet NSPopUpButton* videoDevicesList;
@property (assign) IBOutlet NSPopUpButton* sizesList;
@property (assign) IBOutlet NSPopUpButton* ratesList;
39

40 41
@property BOOL shouldHandlePreview;

42 43 44
@end

@implementation VideoPrefsVC
45 46 47 48 49 50 51 52 53 54 55 56 57
@synthesize previewView;
@synthesize videoDevicesList;
@synthesize sizesList;
@synthesize ratesList;

QMetaObject::Connection frameUpdated;
QMetaObject::Connection previewStarted;
QMetaObject::Connection previewStopped;

- (void)loadView
{
    [super loadView];

58
    // Make sure models are loaded
59 60 61
    Video::ConfigurationProxy::deviceModel().rowCount();
    Video::ConfigurationProxy::resolutionModel().rowCount();
    Video::ConfigurationProxy::rateModel().rowCount();
62

63
    // Prepopulate values
64 65
    QModelIndex qDeviceIdx = Video::ConfigurationProxy::deviceSelectionModel().currentIndex();
    [videoDevicesList addItemWithTitle:Video::ConfigurationProxy::deviceModel().data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
66

67 68
    QModelIndex qSizeIdx = Video::ConfigurationProxy::resolutionSelectionModel().currentIndex();
    [sizesList addItemWithTitle:Video::ConfigurationProxy::resolutionModel().data(qSizeIdx, Qt::DisplayRole).toString().toNSString()];
69

70 71
    QModelIndex qRate = Video::ConfigurationProxy::rateSelectionModel().currentIndex();
    [ratesList addItemWithTitle:Video::ConfigurationProxy::rateModel().data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    // connect to model reset (device may have changed) and selection changed signals
    QObject::connect(qobject_cast<QAbstractProxyModel*>(&Video::ConfigurationProxy::resolutionModel()),
                     &QAbstractProxyModel::modelReset,
                     [=]() {
                         [sizesList removeAllItems];
                     });

    QObject::connect(&Video::ConfigurationProxy::resolutionSelectionModel(),
                     &QItemSelectionModel::currentChanged,
                     [=](const QModelIndex &current, const QModelIndex &previous) {
                         if (!current.isValid()) {
                             return;
                         }
                         [sizesList removeAllItems];
                         [sizesList addItemWithTitle:Video::ConfigurationProxy::resolutionSelectionModel().currentIndex().data(Qt::DisplayRole).toString().toNSString()];

                     });

    QObject::connect(qobject_cast<QAbstractProxyModel*>(&Video::ConfigurationProxy::rateModel()),
                     &QAbstractProxyModel::modelReset,
                     [=]() {
                         [ratesList removeAllItems];
                     });

    QObject::connect(&Video::ConfigurationProxy::rateSelectionModel(),
                     &QItemSelectionModel::currentChanged,
                     [=](const QModelIndex &current, const QModelIndex &previous) {
                         if (!current.isValid()) {
                             return;
                         }
                         [ratesList removeAllItems];
                         [ratesList addItemWithTitle:Video::ConfigurationProxy::rateSelectionModel().currentIndex().data(Qt::DisplayRole).toString().toNSString()];

                     });
107 108 109 110 111 112 113 114 115 116 117

    [previewView setWantsLayer:YES];
    [previewView setLayer:[CALayer layer]];
    [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
    [previewView.layer setContentsGravity:kCAGravityResizeAspect];
    [previewView.layer setFrame:previewView.frame];
    [previewView.layer setBounds:previewView.frame];
}

- (IBAction)chooseDevice:(id)sender {
    int index = [sender indexOfSelectedItem];
118 119
    QModelIndex qIdx = Video::ConfigurationProxy::deviceModel().index(index, 0);
    Video::ConfigurationProxy::deviceSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
120 121 122 123
}

- (IBAction)chooseSize:(id)sender {
    int index = [sender indexOfSelectedItem];
124 125
    QModelIndex qIdx = Video::ConfigurationProxy::resolutionModel().index(index, 0);
    Video::ConfigurationProxy::resolutionSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
126 127 128 129
}

- (IBAction)chooseRate:(id)sender {
    int index = [sender indexOfSelectedItem];
130 131
    QModelIndex qIdx = Video::ConfigurationProxy::rateModel().index(index, 0);
    Video::ConfigurationProxy::rateSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
132 133 134 135 136 137 138
}

- (void) connectPreviewSignals
{
    QObject::disconnect(frameUpdated);
    QObject::disconnect(previewStopped);
    QObject::disconnect(previewStarted);
139

140
    previewStarted = QObject::connect(&Video::PreviewManager::instance(),
141 142 143 144 145 146 147 148 149 150
                                      &Video::PreviewManager::previewStarted,
                                      [=](Video::Renderer* renderer) {
                                          NSLog(@"Preview started");
                                          QObject::disconnect(frameUpdated);
                                          frameUpdated = QObject::connect(renderer,
                                                                          &Video::Renderer::frameUpdated,
                                                                          [=]() {
                                                                              [self renderer:Video::PreviewManager::instance().previewRenderer() renderFrameForView:previewView];
                                                                          });
                                      });
151

152
    previewStopped = QObject::connect(&Video::PreviewManager::instance(),
153 154 155 156 157 158
                                      &Video::PreviewManager::previewStopped,
                                      [=](Video::Renderer* renderer) {
                                          NSLog(@"Preview stopped");
                                          QObject::disconnect(frameUpdated);
                                          [previewView.layer setContents:nil];
                                      });
159

160
    frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
161 162 163
                                    &Video::Renderer::frameUpdated,
                                    [=]() {
                                        [self renderer:Video::PreviewManager::instance().previewRenderer()
164
                                                  renderFrameForView:previewView];
165
                                    });
166 167 168 169 170 171
}

-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
{
    QSize res = renderer->size();

172 173 174 175
    auto frame_ptr = renderer->currentFrame();
    auto frame_data = frame_ptr.ptr;
    if (!frame_data)
        return;
176 177

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
178
    CGContextRef newContext = CGBitmapContextCreate(frame_data,
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
                                                    res.width(),
                                                    res.height(),
                                                    8,
                                                    4*res.width(),
                                                    colorSpace,
                                                    kCGImageAlphaPremultipliedLast);


    CGImageRef newImage = CGBitmapContextCreateImage(newContext);

    /*We release some components*/
    CGContextRelease(newContext);
    CGColorSpaceRelease(colorSpace);

    [CATransaction begin];
    view.layer.contents = (__bridge id)newImage;
    [CATransaction commit];

    CFRelease(newImage);
}

200 201
- (void) viewWillAppear
{
202 203 204 205
    // check if preview has to be started/stopped by this controller
    self.shouldHandlePreview = !Video::PreviewManager::instance().previewRenderer()->isRendering();

    [self connectPreviewSignals];
206
    if (self.shouldHandlePreview) {
207
        Video::PreviewManager::instance().startPreview();
208
    }
209 210 211 212
}

- (void)viewWillDisappear
{
213 214 215
    QObject::disconnect(frameUpdated);
    QObject::disconnect(previewStopped);
    QObject::disconnect(previewStarted);
216
    if (self.shouldHandlePreview) {
217
        Video::PreviewManager::instance().stopPreview();
218
    }
219 220 221 222 223 224 225
}

#pragma mark - NSMenuDelegate methods

- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
{
    QModelIndex qIdx;
Alexandre Lision's avatar
Alexandre Lision committed
226
    if(self.videoDevicesList.menu == menu) {
227 228
        qIdx = Video::ConfigurationProxy::deviceModel().index(index, 0);
        [item setTitle:Video::ConfigurationProxy::deviceModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
229 230 231
        if (qIdx == Video::ConfigurationProxy::deviceSelectionModel().currentIndex()) {
            [videoDevicesList selectItem:item];
        }
Alexandre Lision's avatar
Alexandre Lision committed
232
    } else if(self.sizesList.menu == menu) {
233 234
        qIdx = Video::ConfigurationProxy::resolutionModel().index(index, 0);
        [item setTitle:Video::ConfigurationProxy::resolutionModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
235 236 237
        if (qIdx == Video::ConfigurationProxy::resolutionSelectionModel().currentIndex()) {
            [sizesList selectItem:item];
        }
238

Alexandre Lision's avatar
Alexandre Lision committed
239
    } else if(self.ratesList.menu == menu) {
240 241
        qIdx = Video::ConfigurationProxy::rateModel().index(index, 0);
        [item setTitle:Video::ConfigurationProxy::rateModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
242 243 244
        if (qIdx == Video::ConfigurationProxy::rateSelectionModel().currentIndex()) {
            [ratesList selectItem:item];
        }
245 246 247
    }
    return YES;
}
248

249 250
- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
{
Alexandre Lision's avatar
Alexandre Lision committed
251
    if(self.videoDevicesList.menu == menu) {
252
        return Video::ConfigurationProxy::deviceModel().rowCount();
Alexandre Lision's avatar
Alexandre Lision committed
253
    } else if(self.sizesList.menu == menu) {
254
        return Video::ConfigurationProxy::resolutionModel().rowCount();
Alexandre Lision's avatar
Alexandre Lision committed
255
    } else if(self.ratesList.menu == menu) {
256
        return Video::ConfigurationProxy::rateModel().rowCount();
257
    }
Alexandre Lision's avatar
Alexandre Lision committed
258
    return 0;
259 260 261
}

@end