VideoPrefsVC.mm 11.1 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
@property (assign) IBOutlet NSButton *enableHardwareAccelerationButton;
40

41 42
@property BOOL shouldHandlePreview;

43 44 45
@end

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

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

- (void)loadView
{
    [super loadView];

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

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

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

71 72
    QModelIndex qRate = Video::ConfigurationProxy::rateSelectionModel().currentIndex();
    [ratesList addItemWithTitle:Video::ConfigurationProxy::rateModel().data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
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 107
    // 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()];

                     });
108 109 110 111 112 113 114

    [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];
115 116 117

    [self.enableHardwareAccelerationButton setState:Video::ConfigurationProxy::getDecodingAccelerated()];

118 119 120 121
}

- (IBAction)chooseDevice:(id)sender {
    int index = [sender indexOfSelectedItem];
122 123
    QModelIndex qIdx = Video::ConfigurationProxy::deviceModel().index(index, 0);
    Video::ConfigurationProxy::deviceSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
124 125 126 127
}

- (IBAction)chooseSize:(id)sender {
    int index = [sender indexOfSelectedItem];
128 129
    QModelIndex qIdx = Video::ConfigurationProxy::resolutionModel().index(index, 0);
    Video::ConfigurationProxy::resolutionSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
130 131 132 133
}

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

138 139 140 141 142
- (IBAction)toggleHardwareAcceleration:(NSButton *)sender {
    bool enabled = [sender state]==NSOnState;
    Video::ConfigurationProxy::setDecodingAccelerated(enabled);
}

143 144 145 146 147
- (void) connectPreviewSignals
{
    QObject::disconnect(frameUpdated);
    QObject::disconnect(previewStopped);
    QObject::disconnect(previewStarted);
148

149
    previewStarted = QObject::connect(&Video::PreviewManager::instance(),
150 151 152 153 154 155 156 157 158 159
                                      &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];
                                                                          });
                                      });
160

161
    previewStopped = QObject::connect(&Video::PreviewManager::instance(),
162 163 164 165 166 167
                                      &Video::PreviewManager::previewStopped,
                                      [=](Video::Renderer* renderer) {
                                          NSLog(@"Preview stopped");
                                          QObject::disconnect(frameUpdated);
                                          [previewView.layer setContents:nil];
                                      });
168

169
    frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
170 171 172
                                    &Video::Renderer::frameUpdated,
                                    [=]() {
                                        [self renderer:Video::PreviewManager::instance().previewRenderer()
173
                                                  renderFrameForView:previewView];
174
                                    });
175 176 177 178 179 180
}

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

181 182 183 184
    auto frame_ptr = renderer->currentFrame();
    auto frame_data = frame_ptr.ptr;
    if (!frame_data)
        return;
185 186

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
187
    CGContextRef newContext = CGBitmapContextCreate(frame_data,
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
                                                    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);
}

209 210
- (void) viewWillAppear
{
211 212 213 214
    // check if preview has to be started/stopped by this controller
    self.shouldHandlePreview = !Video::PreviewManager::instance().previewRenderer()->isRendering();

    [self connectPreviewSignals];
215
    if (self.shouldHandlePreview) {
216
        Video::PreviewManager::instance().startPreview();
217
    }
218 219 220 221
}

- (void)viewWillDisappear
{
222 223 224
    QObject::disconnect(frameUpdated);
    QObject::disconnect(previewStopped);
    QObject::disconnect(previewStarted);
225
    if (self.shouldHandlePreview) {
226
        Video::PreviewManager::instance().stopPreview();
227
    }
228 229 230 231 232 233 234
}

#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
235
    if(self.videoDevicesList.menu == menu) {
236 237
        qIdx = Video::ConfigurationProxy::deviceModel().index(index, 0);
        [item setTitle:Video::ConfigurationProxy::deviceModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
238 239 240
        if (qIdx == Video::ConfigurationProxy::deviceSelectionModel().currentIndex()) {
            [videoDevicesList selectItem:item];
        }
Alexandre Lision's avatar
Alexandre Lision committed
241
    } else if(self.sizesList.menu == menu) {
242 243
        qIdx = Video::ConfigurationProxy::resolutionModel().index(index, 0);
        [item setTitle:Video::ConfigurationProxy::resolutionModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
244 245 246
        if (qIdx == Video::ConfigurationProxy::resolutionSelectionModel().currentIndex()) {
            [sizesList selectItem:item];
        }
247

Alexandre Lision's avatar
Alexandre Lision committed
248
    } else if(self.ratesList.menu == menu) {
249 250
        qIdx = Video::ConfigurationProxy::rateModel().index(index, 0);
        [item setTitle:Video::ConfigurationProxy::rateModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
251 252 253
        if (qIdx == Video::ConfigurationProxy::rateSelectionModel().currentIndex()) {
            [ratesList selectItem:item];
        }
254 255 256
    }
    return YES;
}
257

258 259
- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
{
Alexandre Lision's avatar
Alexandre Lision committed
260
    if(self.videoDevicesList.menu == menu) {
261
        return Video::ConfigurationProxy::deviceModel().rowCount();
Alexandre Lision's avatar
Alexandre Lision committed
262
    } else if(self.sizesList.menu == menu) {
263
        return Video::ConfigurationProxy::resolutionModel().rowCount();
Alexandre Lision's avatar
Alexandre Lision committed
264
    } else if(self.ratesList.menu == menu) {
265
        return Video::ConfigurationProxy::rateModel().rowCount();
266
    }
Alexandre Lision's avatar
Alexandre Lision committed
267
    return 0;
268 269 270
}

@end