Skip to content

Commit

Permalink
Implemented HTML5 video capture and fixed webcam image upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Felicitus committed Jul 18, 2015
1 parent a1c41b3 commit ba8c40a
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 69 deletions.
36 changes: 32 additions & 4 deletions app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,21 @@ services:
"hydra:title": "A custom operation"
"returns": "xmls:string"

resource.tempimage.collection_operation.custom_post_webcam:
class: "Dunglas\ApiBundle\Api\Operation\Operation"
public: false
factory: [ "@api.operation_factory", "createCollectionOperation" ]
arguments:
- "@resource.tempimage" # Resource
- [ "POST" ] # Methods
- "/temp_images/webcamUpload" # Path
- "PartKeeprImageBundle:TemporaryImage:webcamUpload" # Controller
- "TemporaryImageUploadWebcam" # Route name
- # Context (will be present in Hydra documentation)
"@type": "hydra:Operation"
"hydra:title": "A custom operation"
"returns": "xmls:string"

resource.tempimage.item_operation.custom_get:
class: "Dunglas\ApiBundle\Api\Operation\Operation"
public: false
Expand All @@ -430,7 +445,7 @@ services:
tags: [ { name: "api.resource" } ]
calls:
- method: "initCollectionOperations"
arguments: [ [ "@resource.tempimage.collection_operation.custom_post" ] ]
arguments: [ [ "@resource.tempimage.collection_operation.custom_post", "@resource.tempimage.collection_operation.custom_post_webcam" ] ]
- method: "initItemOperations"
arguments: [ [ "@resource.tempimage.item_operation.custom_get" ] ]

Expand All @@ -449,6 +464,21 @@ services:
"hydra:title": "A custom operation"
"returns": "xmls:string"

resource.tempfile.collection_operation.custom_post_webcam:
class: "Dunglas\ApiBundle\Api\Operation\Operation"
public: false
factory: [ "@api.operation_factory", "createCollectionOperation" ]
arguments:
- "@resource.tempfile" # Resource
- [ "POST" ] # Methods
- "/temp_uploaded_files/webcamUpload" # Path
- "PartKeeprUploadedFileBundle:TemporaryFile:webcamUpload" # Controller
- "TemporaryFileUploadWebcam" # Route name
- # Context (will be present in Hydra documentation)
"@type": "hydra:Operation"
"hydra:title": "A custom operation"
"returns": "xmls:string"

resource.tempfile.item_operation.get:
class: "Dunglas\ApiBundle\Api\Operation\Operation"
public: false
Expand Down Expand Up @@ -485,8 +515,6 @@ services:
"hydra:title": "A custom operation"
"returns": "xmls:string"



resource.tempfile:
parent: "api.resource"
arguments: [ "PartKeepr\\UploadedFileBundle\\Entity\\TempUploadedFile" ]
Expand All @@ -495,4 +523,4 @@ services:
- method: "initCollectionOperations"
arguments: [ [ "@resource.tempfile.collection_operation.custom_post" ] ]
- method: "initItemOperations"
arguments: [ [ "@resource.tempfile.item_operation.get", "@resource.tempfile.item_operation.custom_get", "@resource.tempfile.item_operation.custom_get_mimetype" ] ]
arguments: [ [ "@resource.tempfile.item_operation.get", "@resource.tempfile.item_operation.custom_get", "@resource.tempfile.item_operation.custom_get_mimetype", "@resource.tempfile.collection_operation.custom_post_webcam" ] ]
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,17 @@ Ext.define('PartKeepr.AttachmentGrid', {
},
onWebcamClick: function () {
var wp = Ext.create("PartKeepr.WebcamPanel");
wp.on("uploadComplete", this.onFileUploaded, this);
wp.on("fileUploaded", this.onFileUploaded, this);

var j = Ext.create("Ext.window.Window", {
title: i18n("Take Webcam Photo"),
layout: 'fit',
items: [
wp
]
});

wp.on("uploadComplete", function () { j.close(); });
wp.on("fileUploaded", function () { j.close(); });

j.show();
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,91 @@
* a flash (jpegcam).
*/
Ext.define('PartKeepr.WebcamPanel', {
extend: 'Ext.panel.Panel',
alias: 'widget.WebcamPanel',
initComponent: function () {

this.takePhotoButton = Ext.create("Ext.button.Button", {
text: i18n("Take picture and upload"),
icon: 'bundles/brainbitsfugueicons/icons/fugue/16/webcam.png',
handler: this.takePhoto
});

// Create a toolbar with the "take photo" button
this.bbar = Ext.create("Ext.toolbar.Toolbar", {
enableOverflow: true,
items: [ this.takePhotoButton ]
});

// Render the SWF
this.on("afterrender", this.renderWebcam, this);

this.callParent();
},
/**
* Renders the webcam swf.
* @param e The element for this component
*/
renderWebcam: function (e) {
webcam.set_swf_url("resources/webcam.swf");
webcam.set_quality(90);
webcam.set_api_url(PartKeepr.getBasePath()+"?service=TempFile&call=uploadCam&session="+PartKeepr.getApplication().getSession());
webcam.set_shutter_sound(false);
webcam.set_hook('onComplete', Ext.bind(this.onUploadComplete, this));

e.body.insertHtml('beforeEnd', webcam.get_html(640,480, 640, 480));
},
/**
* Takes a photo using the webcam.
*/
takePhoto: function () {
webcam.snap();
this.takePhotoButton.disable();
this.takePhotoButton.setText(i18n("Uploading..."));
},
/**
* Called when the upload is complete. Resumes webcam operation
* and fires the event. 'uploadComplete'
* @param message The server side message
*/
onUploadComplete: function (message) {
var response = Ext.decode(message);

webcam.reset();
this.fireEvent("uploadComplete", response.response);

}
extend: 'Ext.panel.Panel',
alias: 'widget.WebcamPanel',

layout: 'fit',
width: 320,
height: 286,
items: [{
xtype: 'component',
itemId: 'video',
autoEl: {
tag: 'video',
autoplay: 'true'
}
},{
xtype: 'component',
itemId: 'canvas',
autoEl: {
tag: 'canvas',
}
}],
video: null,

initComponent: function ()
{
this.takePhotoButton = Ext.create("Ext.button.Button", {
text: i18n("Take picture and upload"),
icon: 'bundles/brainbitsfugueicons/icons/fugue/16/webcam.png',
handler: this.takePhoto,
scope: this
});

// Create a toolbar with the "take photo" button
this.bbar = Ext.create("Ext.toolbar.Toolbar", {
enableOverflow: true,
items: [this.takePhotoButton]
});


this.callParent();

this.on("afterrender", this._onAfterRender, this);
},
handleVideo: function (stream) {
this.video.src = window.URL.createObjectURL(stream);
},
videoError: function () {
// @todo: Implement video error handler
},
_onAfterRender: function () {
this.video = this.down("#video").getEl().dom;
this.canvas = this.down("#canvas").getEl().dom;

navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;

if (navigator.getUserMedia) {
navigator.getUserMedia({video: true}, Ext.bind(this.handleVideo, this), Ext.bind(this.videoError, this));
}
},
/**
* Takes a photo using the webcam.
*/
takePhoto: function ()
{
this.canvas.width = this.video.videoWidth;
this.canvas.height = this.video.videoHeight;

var ctx = this.canvas.getContext('2d');
ctx.drawImage(this.video, 0, 0, this.video.videoWidth, this.video.videoHeight);

Ext.Ajax.request({
// Might need to adjust the path, depending on if we are uploading a file or image
url: PartKeepr.getBasePath() + "/api/temp_uploaded_files/webcamUpload",
params: this.canvas.toDataURL(),
success: function (response) {
var responseObject = Ext.decode(response.responseText);
this.fireEvent("fileUploaded", responseObject);
},
//@todo implement failure handler
scope: this
});

this.takePhotoButton.disable();
this.takePhotoButton.setText(i18n("Uploading..."));
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,36 @@ public function uploadAction(Request $request)
return new JsonResponse(new TemporaryImageUploadResponse($serializedData));
}

/**
* Uploads a webcam image
*
* @param Request $request The request to process
* @return Response
*/
public function webcamUploadAction(Request $request)
{
$image = new TempImage();
$imageService = $this->get("partkeepr_image_service");


$data = $request->getContent();

$base64 = explode(',', $data);
$imageService->replaceFromData($image, base64_decode($base64[1]), "webcam.png");

$this->getDoctrine()->getManager()->persist($image);
$this->getDoctrine()->getManager()->flush();

$resource = $this->getResource($request);

return $this->getSuccessResponse($resource, $image, 201);
}

/**
* @inheritdoc
*/
public function getEntityClass()
{
return "PartKeepr\\ImageBundle\\Entity\\TempImage";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,30 @@ public function uploadAction(Request $request)
return new JsonResponse(new TemporaryImageUploadResponse($serializedData));
}

/**
* Uploads a webcam image
*
* @param Request $request The request to process
* @return Response
*/
public function webcamUploadAction(Request $request)
{
$file = new TempUploadedFile();
$fileService = $this->get("partkeepr_uploadedfile_service");


$data = $request->getContent();

$base64 = explode(',', $data);
$fileService->replaceFromData($file, base64_decode($base64[1]), "webcam.png");

$this->getDoctrine()->getManager()->persist($file);
$this->getDoctrine()->getManager()->flush();

$resource = $this->getResource($request);

return $this->getSuccessResponse($resource, $file, 201);
}

/**
* @inheritdoc
Expand Down
21 changes: 13 additions & 8 deletions src/PartKeepr/UploadedFileBundle/Services/UploadedFileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ public function replaceFromFilesystem(UploadedFile $file, File $filesystemFile)
}
}

public function replaceFromData(UploadedFile $file, $data, $filename)
{
$tempName = tempnam("/tmp", "PARTKEEPR");

file_put_contents($tempName, $data);

$this->replaceFromFilesystem($file, new File($tempName));
$file->setOriginalFilename($filename);

unlink($tempName);
}

/**
* Replaces an existing uploaded file with another uploaded file.
*
Expand Down Expand Up @@ -135,14 +147,7 @@ public function replaceFromURL(UploadedFile $file, $url)

curl_close($curl);

$tempName = tempnam("/tmp", "PARTKEEPR");

file_put_contents($tempName, $data);

$this->replaceFromFilesystem($file, new File($tempName));
$file->setOriginalFilename(basename($url));

unlink($tempName);
$this->replaceFromData($file, $data, basename($url));
}

/**
Expand Down

0 comments on commit ba8c40a

Please sign in to comment.