使用C语言扩展Python(五)

上一篇中我们在python端的做法是每次读取一个数据块,然后将这个数据块传递进C扩展模块中去,但对于目标文件的数据写入是在C扩展模块中完成的,但其实可以更面向对象一点,不是吗?原来outfp是一个文件指针,
不如改成一个从Python中传递一个文件对象到C模块里去,这个文件对象有自己的write方法,这样在C扩展模块中你就可以回调文件对象的write方法来完成数据的写入。
首先来看Python端的代码,我们定义了一个file类继承下来的MyFile子类,其中的write方法就是为在C扩展模块中回调而专门准备的。
代码
#!/usr/bin/env python

import clame

INBUFSIZE 
= 4096

class MyFile(file):

    
def __init__(self, path, mode):
        file.
__init__(self, path, mode)
        self.n 
= 0

    
def write(self, s):
        file.write(self, s)
        self.n 
+= 1

output 
= MyFile('test3.mp3''wb')
encoder 
= clame.Encoder(output)
input 
= file('test.raw''rb')

data 
= input.read(INBUFSIZE)
while data != '':
    encoder.encode(data)
    data 
= input.read(INBUFSIZE)

input.close()
encoder.close()
output.close()

print 'output.write was called %d times' % output.n

  再来看C模块的代码,clame_EncoderObject结构体中的outfp改成了PyObject类型的指针,相应的dealloc方法也做了调整,由于outfp是一个对象,因此需要对其引用计数进行减1操作,而以前的代码是直接调用fclose来直接关闭打开的目标文件。但现在我们只需要对其引用计数做减1操作,等到其为0的时候,在外部的python代码中就可以关闭这个文件对象。同样可以看到在init函数中有相应的引用计数加1的操作。在encode和close两个函数中,通过PyObject_CallMethod实现了对Python对象中指定方法的回调。

代码
#include <Python.h>

#include 
<lame.h>

/*
 * On Linux:
 *   gcc -shared -I/usr/include/python2.6 -I/usr/local/include/lame clame.c \
 *   -lmp3lame -o clame.so
 *
 *
 
*/

typedef 
struct {
    PyObject_HEAD
    PyObject 
*outfp;
    lame_global_flags 
*gfp;
} clame_EncoderObject;

static PyObject *Encoder_new(PyTypeObject *type, PyObject *args, PyObject *kw) {
    clame_EncoderObject 
*self = (clame_EncoderObject *)type->tp_alloc(type, 0);
    self
->outfp = NULL;
    self
->gfp = NULL;
    
return (PyObject *)self;
}

static void Encoder_dealloc(clame_EncoderObject *self) {
    
if (self->gfp) {
        lame_close(self
->gfp);
    }
    Py_XDECREF(self
->outfp);
    self
->ob_type->tp_free(self);
}

static int Encoder_init(clame_EncoderObject *self, PyObject *args, PyObject *kw) {
    PyObject 
*outfp;
    
if (!PyArg_ParseTuple(args, "O"&outfp)) {
        
return -1;
    }
    
if (self->outfp || self->gfp) {
        PyErr_SetString(PyExc_Exception, 
"__init__ already called");
        
return -1;
    }
    self
->outfp = outfp;
    Py_INCREF(self
->outfp);
    self
->gfp = lame_init();
    lame_init_params(self
->gfp);
    
return 0;
}

static PyObject *Encoder_encode(clame_EncoderObject *self, PyObject *args) {
    
char *in_buffer;
    
int in_length;
    
int mp3_length;
    
char *mp3_buffer;
    
int mp3_bytes;
    
if (!(self->outfp && self->gfp)) {
        PyErr_SetString(PyExc_Exception, 
"encoder not open");
        
return NULL;
    }
    
if (!PyArg_ParseTuple(args, "s#"&in_buffer, &in_length)) {
        
return NULL;
    }
    in_length 
/= 2;
    mp3_length 
= (int)(1.25 * in_length) + 7200;
    mp3_buffer 
= (char *)malloc(mp3_length);
    
if (in_length > 0) {
        mp3_bytes 
= lame_encode_buffer_interleaved(
            self
->gfp,
            (
short *)in_buffer,
            in_length 
/ 2,
            mp3_buffer,
            mp3_length
        );
        
if (mp3_bytes > 0) {
        PyObject
* write_result = PyObject_CallMethod(
                                     self
->outfp, "write""(s#)", mp3_buffer, mp3_bytes);
        
if (!write_result) {
            free(mp3_buffer);
        
return NULL;
        }
        Py_DECREF(write_result);
        }
    }
    free(mp3_buffer);
    Py_RETURN_NONE;
}

static PyObject *Encoder_close(clame_EncoderObject *self) {
    
int mp3_length;
    
char *mp3_buffer;
    
int mp3_bytes;
    
if (!(self->outfp && self->gfp)) {
        PyErr_SetString(PyExc_Exception, 
"encoder not open");
        
return NULL;
    }
    mp3_length 
= 7200;
    mp3_buffer 
= (char *)malloc(mp3_length);
    mp3_bytes 
= lame_encode_flush(self->gfp, mp3_buffer, sizeof(mp3_buffer));
    
if (mp3_bytes > 0) {
        PyObject
* write_result = PyObject_CallMethod(
                                 self
->outfp, "write""(s#)", mp3_buffer, mp3_bytes);
    
if (!write_result) {
        free(mp3_buffer);
        
return NULL;
    }
    Py_DECREF(write_result);
    }
    free(mp3_buffer);
    lame_close(self
->gfp);
    self
->gfp = NULL;
    self
->outfp = NULL;
    Py_RETURN_NONE;
}

static PyMethodDef Encoder_methods[] = {
    { 
"encode", (PyCFunction)Encoder_encode, METH_VARARGS,
          
"Encodes and writes data to the output file." },
    { 
"close", (PyCFunction)Encoder_close, METH_NOARGS,
          
"Closes the output file." },
    { NULL, NULL, 
0, NULL }
};

static PyTypeObject clame_EncoderType = {
    PyObject_HEAD_INIT(NULL)
    
0,                             /* ob_size */
    
"clame.Encoder",             /* tp_name */
    
sizeof(clame_EncoderObject), /* tp_basicsize */
    
0,                             /* tp_itemsize */
    (destructor)Encoder_dealloc,   
/* tp_dealloc */
    
0,                             /* tp_print */
    
0,                             /* tp_getattr */
    
0,                             /* tp_setattr */
    
0,                             /* tp_compare */
    
0,                             /* tp_repr */
    
0,                             /* tp_as_number */
    
0,                             /* tp_as_sequence */
    
0,                             /* tp_as_mapping */
    
0,                             /* tp_hash */
    
0,                             /* tp_call */
    
0,                             /* tp_str */
    
0,                             /* tp_getattro */
    
0,                             /* tp_setattro */
    
0,                             /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,            
/* tp_flags */
    
"My first encoder object.",    /* tp_doc */
    
0,                             /* tp_traverse */
    
0,                             /* tp_clear */
    
0,                             /* tp_richcompare */
    
0,                             /* tp_weaklistoffset */
    
0,                             /* tp_iter */
    
0,                             /* tp_iternext */
    Encoder_methods,               
/* tp_methods */
    
0,                             /* tp_members */
    
0,                             /* tp_getset */
    
0,                             /* tp_base */
    
0,                             /* tp_dict */
    
0,                             /* tp_descr_get */
    
0,                             /* tp_descr_set */
    
0,                             /* tp_dictoffset */
    (initproc)Encoder_init,        
/* tp_init */
    
0,                             /* tp_alloc */
    Encoder_new,                   
/* tp_new */
    
0,                             /* tp_free */
};

static PyMethodDef clame_methods[] = {
    { NULL, NULL, 
0, NULL }
};

PyMODINIT_FUNC initclame() {
    PyObject 
*m;
    
if (PyType_Ready(&clame_EncoderType) < 0) {
        
return;
    }
    m 
= Py_InitModule3("clame", clame_methods, "My third LAME module.");
    Py_INCREF(
&clame_EncoderType);
    PyModule_AddObject(m, 
"Encoder", (PyObject *)&clame_EncoderType);
}

 

 

posted on 2010-05-23 23:15  Phinecos(洞庭散人)  阅读(4323)  评论(2编辑  收藏  举报

导航