renamed framecapture utility source. refactored code and fixed some bugs

next step to coordinate cross-platform integration

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@3790 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
chrisparker126 2010-11-14 11:59:35 +00:00
parent 624aef2065
commit 696cfe3a29
4 changed files with 416 additions and 440 deletions

View File

@ -1,373 +0,0 @@
/****************************************************************
* This file is distributed under the following license:
*
* Copyright (c) 2006 Raul E.
* Copyright (c) 2010 Christopher Evi-Parker
*
* 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 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.
*
* 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.
*
*************************************************************************/
#include "filefunctions.h"
QString filefunctions::cfilename=NULL;
QTreeWidget *filefunctions::treeObj=NULL;
QStringList filefunctions::tempimages;
QString filefunctions::codec=NULL;
int filefunctions::length=0;
int filefunctions::fwidth=0;
int filefunctions::fheight=0;
filefunctions::filefunctions(){
// clear tempfiles
for ( int i=0; i<filefunctions::tempimages.size();i++){
QFile::remove (filefunctions::tempimages.at(i));
}
filefunctions::tempimages.clear();
}
filefunctions::~filefunctions(){
}
QString filefunctions::getCodec(){
return filefunctions::codec;
}
QString filefunctions::getImageWidth(){
return QString::number(filefunctions::fwidth);
}
QString filefunctions::getImageHeight(){
return QString::number(filefunctions::fheight);
}
int filefunctions::getLength(){ //ms
return filefunctions::length;
}
void filefunctions::open(){
vo_driver="auto";
//ao_driver="auto";
tempdir=QDir::tempPath ();
xine= xine_new();
xine_init(xine);
if((vo_port=xine_open_video_driver(xine,vo_driver,XINE_VISUAL_TYPE_NONE, NULL))==NULL)
qWarning("ERROR - OPEN VIDEO DRIVER");
//ao_port=xine_open_audio_driver(xine,ao_driver,NULL);
//stream = xine_stream_new(xine,ao_port,vo_port);
stream = xine_stream_new(xine,NULL,vo_port);
if(!xine_open(stream,cfilename.toLocal8Bit())){
const int errorCode= xine_get_error(stream);
QMessageBox::critical(0,"QFrameCatcher","ERROR.\n" + getXineError(errorCode));
return ;
}
if(xine_get_pos_length(stream,0,0,&length)){
//std::cout << length<< std::endl;
}
xine_play(stream,0,0);
const uint8_t option= 1;
switch (option){
case 1 :
getFrames(); // Capture x Frames
break;
case 2 :
getFramesTime(); // Capture every x seconds
break;
default :
getFrames();
}
xine_close(stream);
xine_dispose(stream);
xine_close_video_driver(xine,vo_port);
//xine_close_audio_driver(xine,ao_port);
}
void filefunctions::saveRGBImage(uchar *rgb32BitData,int pos,int width,int height){
currImage = new QImage(rgb32BitData, width, height,QImage::Format_RGB32);
//delete rgb32BitData;
}
void filefunctions::savebuffer(QByteArray &ba , QImage *image){
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image->save(&buffer, "PNG");
}
bool filefunctions::saveimage(const QByteArray &ba , const QString &filename){
bool correct=true;
QFile image(filename);
if(!image.open(QIODevice::WriteOnly) )
return false;
if(image.write(ba)==-1)
correct=false;
image.close();
return correct;
}
void filefunctions::getFramesTime(){
xine_play(stream,0,0);
codec=xine_get_meta_info(stream, XINE_META_INFO_VIDEOCODEC);
const uint32_t chunk= (30) * 1000; // mseconds
// Progress Dialog
QString progresdialogtext =QApplication::translate("xine","Generating Thumbnails...",0,QApplication::UnicodeUTF8);
QString cancelbuttontext=QApplication::translate("xine","&Cancel",0,QApplication::UnicodeUTF8);
progress = new QProgressDialog(progresdialogtext , cancelbuttontext , 0, length,0,Qt::WindowSystemMenuHint);
for (int32_t frame=chunk ; frame<length ; frame=frame+chunk){
xine_play(stream,0,frame); // 1 frame 2 mseconds
SaveFrame(frame);
progress->setValue(frame);
qApp->processEvents();
if(progress->wasCanceled())
break;
}
progress->setValue(length);
delete progress;
}
void filefunctions::getFrames(){
xine_play(stream,0,0);
codec=xine_get_meta_info(stream, XINE_META_INFO_VIDEOCODEC);
int frame = 20;
xine_play(stream,0,frame); // 1 frame 2 mseconds
SaveFrame(frame);
}
void filefunctions::yuy2Toyv12 (uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height){
int i, j, w2;
w2 = width / 2;
for (i = 0; i < height; i += 2){
for (j = 0; j < w2; j++){
/*
* packed YUV 422 is: Y[i] U[i] Y[i+1] V[i]
*/
*(y++) = *(input++);
*(u++) = *(input++);
*(y++) = *(input++);
*(v++) = *(input++);
}
/*
* down sampling
*/
for (j = 0; j < w2; j++){
/*
* skip every second line for U and V
*/
*(y++) = *(input++);
input++;
*(y++) = *(input++);
input++;
}
}
}
uchar * filefunctions::yv12ToRgb (uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height){
/*
* Create rgb data from yv12
*/
#define clip_8_bit(val) \
{ \
if (val < 0) \
val = 0; \
else \
if (val > 255) val = 255; \
}
int i, j;
int y, u, v;
int r, g, b;
int sub_i_uv;
int sub_j_uv;
int uv_width, uv_height;
uchar *rgb;
uv_width = width / 2;
uv_height = height / 2;
rgb = new uchar[(width * height * 4)]; //qt needs a 32bit align
if (!rgb) //qDebug ("Not enough memory!");
return NULL;
for (i = 0; i < height; ++i){
/*
* calculate u & v rows
*/
sub_i_uv = ((i * uv_height) / height);
for (j = 0; j < width; ++j){
/*
* calculate u & v columns
*/
sub_j_uv = ((j * uv_width) / width);
/***************************************************
*
* Colour conversion from
* http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
*
* Thanks to Billy Biggs <vektor@dumbterm.net>
* for the pointer and the following conversion.
*
* R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ])
* G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ])
* B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ])
*
* Where in xine the above values are represented as
*
* Y' == image->y
* Cb == image->u
* Cr == image->v
*
***************************************************/
y = src_y[(i * width) + j] - 16;
u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128;
v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128;
r = (int)((1.1644 * (double)y) + (1.5960 * (double)v));
g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v));
b = (int)((1.1644 * (double)y) + (2.0172 * (double)u));
clip_8_bit (r);
clip_8_bit (g);
clip_8_bit (b);
rgb[(i * width + j) * 4 + 0] = b;
rgb[(i * width + j) * 4 + 1] = g;
rgb[(i * width + j) * 4 + 2] = r;
rgb[(i * width + j) * 4 + 3] = 0;
}
}
return rgb;
}
void filefunctions::SaveFrame(int pos){
uint8_t *yuv = NULL, *y = NULL, *u = NULL, *v =NULL ;
int width, height, ratio, format;
// double desired_ratio, image_ratio;
if (!xine_get_current_frame(stream, &width, &height, &ratio, &format, NULL))
return;
yuv = new uint8_t[((width+8) * (height+1) * 2)];
if (yuv == NULL){
qWarning("Not enough memory to make screenshot!");
return;
}
xine_get_current_frame(stream, &width, &height, &ratio, &format, yuv);
fwidth=width;
fheight=height;
/*
* convert to yv12 if necessary
*/
switch (format){
case XINE_IMGFMT_YUY2:
{
uint8_t *yuy2 = yuv;
yuv = new uint8_t[(width * height * 2)];
if (yuv == NULL){
qWarning("Not enough memory to make screenshot!");
return;
}
y = yuv;
u = yuv + width * height;
v = yuv + width * height * 5 / 4;
yuy2Toyv12 (y, u, v, yuy2, width, height);
delete [] yuy2;
//free (yuy2);
}
break;
case XINE_IMGFMT_YV12:
y = yuv;
u = yuv + width * height;
v = yuv + width * height * 5 / 4;
break;
default:
{
qWarning("Screenshot: Format %s not supported!",(char *) &format);
delete [] yuv;
return;
}
}
uchar* rgb32BitData = yv12ToRgb (y, u, v, width, height);
delete [] yuv;
saveRGBImage(rgb32BitData,pos,width,height);
}
QTime filefunctions::postime(const int itime){
QTime time(0,0,0);
time=time.addMSecs(itime);
return time;
}
QString filefunctions::getXineError(int errorCode){
QString error;
switch (errorCode){
case XINE_ERROR_NO_INPUT_PLUGIN:
case XINE_ERROR_NO_DEMUX_PLUGIN: {
error = QApplication::translate("xine","No plugin found to handle this resource",0,QApplication::UnicodeUTF8);
break;
}
case XINE_ERROR_DEMUX_FAILED: {
error = QApplication::translate("xine","Resource seems to be broken",0,QApplication::UnicodeUTF8);
break;
}
case XINE_ERROR_MALFORMED_MRL: {
error = QApplication::translate("xine","Requested resource does not exist",0,QApplication::UnicodeUTF8);
break;
}
case XINE_ERROR_INPUT_FAILED: {
error = QApplication::translate("xine","Resource can not be opened",0,QApplication::UnicodeUTF8);
break;
}
default: {
error = QApplication::translate("xine","Unknown error",0,QApplication::UnicodeUTF8);
break;
}
}
return error;
}

View File

@ -1,67 +0,0 @@
#ifndef FILEFUNCTIONS_H
#define FILEFUNCTIONS_H
#include <xine.h>
#include <xine/xineutils.h>
#include <QString>
#include <QStringList>
#include <QDir>
#include <QTreeWidgetItem>
#include <QTime>
#include <QPixmap>
#include <QIcon>
#include <QImage>
#include <QByteArray>
#include <QBuffer>
#include <QMessageBox>
#include <QProgressDialog>
#include <QApplication>
class filefunctions{
private:
xine_t *xine;
xine_stream_t *stream;
xine_video_port_t *vo_port;
xine_audio_port_t *ao_port;
xine_event_queue_t *event_queue;
char *vo_driver;
char *ao_driver;
QString tempdir;
static QString codec;
static int length; // length of the stream
static int fwidth;
static int fheight;
void saveRGBImage(uchar *,int,int,int);
QString getXineError(int);
QProgressDialog *progress;
void savebuffer(QByteArray &, QImage *);
bool saveimage(const QByteArray & , const QString &);
public:
filefunctions();
~filefunctions();
static QString cfilename;
static QTreeWidget *treeObj;
static QStringList tempimages;
QImage* currImage;
static QString getCodec();
static QString getImageWidth();
static QString getImageHeight();
static int getLength();
void open();
void getFrames();
void getFramesTime();
void yuy2Toyv12 (uint8_t *, uint8_t *, uint8_t *, uint8_t *, int , int );
uchar * yv12ToRgb (uint8_t *, uint8_t *, uint8_t *, int , int );
void SaveFrame(int);
QTime postime(const int);
};
#endif

View File

@ -0,0 +1,336 @@
/****************************************************************
* This file is distributed under the following license:
*
* Copyright (c) 2006 Raul E.
* Copyright (c) 2010 Chris Evi-Parker
*
* 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 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.
*
* 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.
*
*************************************************************************/
#include "framecatcher.h"
#include <iostream>
framecatcher::framecatcher(): xine(NULL), stream(NULL), vo_port(NULL){
// start up drivers
std::string vo_driver = "auto";
xine= xine_new();
xine_init(xine);
if((vo_port=xine_open_video_driver(xine,vo_driver.c_str(),XINE_VISUAL_TYPE_NONE, NULL))==NULL)
std::cerr << "framecatcher::framecatcher() " << "ERROR OPENING VIDEO DRIVER";
}
framecatcher::~framecatcher(){
if(stream != NULL){
close();
}
xine_close_video_driver(xine,vo_port);
}
std::string framecatcher::getCodec(){
return codec;
}
int framecatcher::getLength(){
return length;
}
int framecatcher::open(const std::string& fileName){
int errorCode = 0;
/* ensure resources used previous stream have been released */
if(stream != NULL)
close();
stream = xine_stream_new(xine,NULL,vo_port);
if(stream == NULL)
return 0;
// open stream
if(!xine_open(stream,fileName.c_str())){
errorCode= xine_get_error(stream);
return errorCode;
}
// get length of video file stream is attached to
if(!xine_get_pos_length(stream,0,0,&length)){
errorCode = xine_get_error(stream);
return errorCode;
}
xine_play(stream,0,0);
const char* temp_codec=xine_get_meta_info(stream, XINE_META_INFO_VIDEOCODEC);
if(temp_codec == NULL)
return 0;
codec = temp_codec;
return 1;
}
void framecatcher::close(){
if(stream != NULL){
xine_close(stream);
xine_dispose(stream);
stream = NULL;
}
}
int framecatcher::getRGBImage(int frame, unsigned char*& rgb32BitData, int& width, int& height){
if(stream == NULL)
return 0;
int errCode = 0;
xine_play(stream,0,0);
xine_play(stream,0,frame); // 1 frame 2 mseconds
uint8_t *yuv = NULL, *y = NULL, *u = NULL, *v =NULL ;
int ratio = 0, format = 0;
if (!xine_get_current_frame(stream, &width, &height, &ratio, &format, NULL)){
errCode= xine_get_error(stream);
return errCode;
}
yuv = new uint8_t[((width+8) * (height+1) * 2)];
if (yuv == NULL){
std::cerr << "Out of Memory!";
return 0;
}
if (!xine_get_current_frame(stream, &width, &height, &ratio, &format, yuv)){
errCode= xine_get_error(stream);
if(yuv != NULL)
delete[] yuv;
return errCode;
}
/*
* convert to yv12 if necessary
*/
switch (format){
case XINE_IMGFMT_YUY2:
{
uint8_t *yuy2 = yuv;
yuv = new uint8_t[(width * height * 2)];
if (yuv == NULL){
std::cerr << "Not enough memory to make screenshot!" << std::endl;
return 0;
}
y = yuv;
u = yuv + width * height;
v = yuv + width * height * 5 / 4;
yuy2Toyv12 (y, u, v, yuy2, width, height);
delete [] yuy2;
}
break;
case XINE_IMGFMT_YV12:
y = yuv;
u = yuv + width * height;
v = yuv + width * height * 5 / 4;
break;
default:
{
std::cerr << "Format Not Supported" << std::endl;
if(yuv != NULL)
delete [] yuv;
return 0;
}
}
rgb32BitData = yv12ToRgb (y, u, v, width, height);
delete [] yuv;
return 1;
}
void framecatcher::yuy2Toyv12 (uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height){
int i, j, w2;
w2 = width / 2;
for (i = 0; i < height; i += 2){
for (j = 0; j < w2; j++){
/*
* packed YUV 422 is: Y[i] U[i] Y[i+1] V[i]
*/
*(y++) = *(input++);
*(u++) = *(input++);
*(y++) = *(input++);
*(v++) = *(input++);
}
/*
* down sampling
*/
for (j = 0; j < w2; j++){
/*
* skip every second line for U and V
*/
*(y++) = *(input++);
input++;
*(y++) = *(input++);
input++;
}
}
}
unsigned char * framecatcher::yv12ToRgb (uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height){
/*
* Create rgb data from yv12
*/
#define clip_8_bit(val) \
{ \
if (val < 0) \
val = 0; \
else \
if (val > 255) val = 255; \
}
int i, j;
int y, u, v;
int r, g, b;
int sub_i_uv;
int sub_j_uv;
int uv_width, uv_height;
unsigned char *rgb;
uv_width = width / 2;
uv_height = height / 2;
rgb = new unsigned char[(width * height * 4)]; //qt needs a 32bit align
if (!rgb) //qDebug ("Not enough memory!");
return NULL;
for (i = 0; i < height; ++i){
/*
* calculate u & v rows
*/
sub_i_uv = ((i * uv_height) / height);
for (j = 0; j < width; ++j){
/*
* calculate u & v columns
*/
sub_j_uv = ((j * uv_width) / width);
/***************************************************
*
* Colour conversion from
* http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
*
* Thanks to Billy Biggs <vektor@dumbterm.net>
* for the pointer and the following conversion.
*
* R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ])
* G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ])
* B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ])
*
* Where in xine the above values are represented as
*
* Y' == image->y
* Cb == image->u
* Cr == image->v
*
***************************************************/
y = src_y[(i * width) + j] - 16;
u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128;
v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128;
r = (int)((1.1644 * (double)y) + (1.5960 * (double)v));
g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v));
b = (int)((1.1644 * (double)y) + (2.0172 * (double)u));
clip_8_bit (r);
clip_8_bit (g);
clip_8_bit (b);
rgb[(i * width + j) * 4 + 0] = b;
rgb[(i * width + j) * 4 + 1] = g;
rgb[(i * width + j) * 4 + 2] = r;
rgb[(i * width + j) * 4 + 3] = 0;
}
}
return rgb;
}
void framecatcher::getXineError(int errorCode, std::string& errorStr){
switch (errorCode){
case XINE_ERROR_NO_INPUT_PLUGIN:
case XINE_ERROR_NO_DEMUX_PLUGIN: {
errorStr = "No plugin found to handle this resource";
break;
}
case XINE_ERROR_DEMUX_FAILED: {
errorStr = "Resource seems to be broken";
break;
}
case XINE_ERROR_MALFORMED_MRL: {
errorStr = "Requested resource does not exist";
break;
}
case XINE_ERROR_INPUT_FAILED: {
errorStr = "Resource can not be opened";
break;
}
default: {
errorStr = "Unknown error";
break;
}
}
return;
}

View File

@ -0,0 +1,80 @@
#ifndef FILEFUNCTIONS_H
#define FILEFUNCTIONS_H
#include <xine.h>
#include <xine/xineutils.h>
#include <string>
/*!
* This can be used to retrieve image frames at a specified
* time from video files using the xine library
*
* In the original implementation this class derived from, attributes were declared static,
* Possibly a good reason, as using seperate instances
* of framecatcher can cause a crash if the xine stream is not correctly closed
*/
class framecatcher{
public:
/*!
* video drivers/ports are started up
*/
framecatcher();
/*!
* video drivers/ports closed,
* current stream is closed if open
*/
~framecatcher();
std::string getCodec();
int getLength();
/*!
*
* @param frame time in msecs you want frame to be taken
* @param rgb32BitData 32 bit aligned rgb image format
* @param width of the image data returned
* @param height of the image data returned
*/
int getRGBImage(int frame, unsigned char*& rgb32BitData, int& width, int& height);
/*!
* attaches a xine stream to the file,
* if valid user can then request image frames using RGBImage
* please ensure you close this stream before establishing a new one!
* Not doing may lead to a seg fault, even if you are using a new instance of framecatcher
* @see getRGBImage
* @see close
*/
int open(const std::string& file);
/*!
* closes and disposes current xine stream if open
*/
void close();
private:
xine_t *xine;
static xine_stream_t *stream;
xine_video_port_t *vo_port;
std::string vo_driver;
int length; // length of the stream
void getXineError(int errCode, std::string& errStr);
void yuy2Toyv12 (uint8_t *, uint8_t *, uint8_t *, uint8_t *, int , int );
unsigned char * yv12ToRgb (uint8_t *, uint8_t *, uint8_t *, int , int );
};
#endif