How to convert the code below to swift? Someone help.
I want to convert this cpp code to swift, from project FreeStreamer.
But some C++ struct and some C callback drive me crazy.
Help.
Here is the code from audio_stream.h and audio_stream.cpp
// var
queued_packet_t *m_queuedHead;
queued_packet_t *m_queuedTail;
queued_packet_t *m_playPacket;
std::list <queued_packet_t*> m_processedPackets;
//struct
typedef struct queued_packet {
UInt64 identifier;
AudioStreamPacketDescription desc;
struct queued_packet *next;
char data[];
} queued_packet_t;
//function one
OSStatus Audio_Stream::encoderDataCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) {
Audio_Stream *THIS = (Audio_Stream *)inUserData;
pthread_mutex_trylock(&THIS->m_packetQueueMutex);
// Dequeue one packet per time for the decoder
queued_packet_t *front = THIS->m_playPacket;
if (!front) {
/* Don't deadlock */
AS_LOCK_TRACE("encoderDataCallback 2: unlock\n");
pthread_mutex_unlock(&THIS->m_packetQueueMutex);
pthread_mutex_trylock(&THIS->m_streamStateMutex);
THIS->m_converterRunOutOfData = true;
pthread_mutex_unlock(&THIS->m_streamStateMutex);
*ioNumberDataPackets = 0;
ioData->mBuffers[0].mDataByteSize = 0;
return noErr;
}
*ioNumberDataPackets = 1;
ioData->mBuffers[0].mData = front->data;
ioData->mBuffers[0].mDataByteSize = front->desc.mDataByteSize;
ioData->mBuffers[0].mNumberChannels = THIS->m_srcFormat.mChannelsPerFrame;
if (outDataPacketDescription) {
*outDataPacketDescription = &front->desc;
}
THIS->m_playPacket = front->next;
THIS->m_processedPackets.push_front(front);
AS_LOCK_TRACE("encoderDataCallback 5: unlock\n");
pthread_mutex_unlock(&THIS->m_packetQueueMutex);
return noErr;
}
//function two
void Audio_Stream::streamDataCallback(void *inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions) {
AS_TRACE("%s: inNumberBytes %u, inNumberPackets %u\n", __FUNCTION__, (unsigned int)inNumberBytes, (unsigned int)inNumberPackets);
Audio_Stream *THIS = static_cast<Audio_Stream*>(inClientData);
if (!THIS->m_audioStreamParserRunning) {
AS_TRACE("%s: stray callback detected!\n", __PRETTY_FUNCTION__);
return;
}
for (int i = 0; i < inNumberPackets; i++) {
/* Allocate the packet */
UInt32 size = inPacketDescriptions[i].mDataByteSize;
queued_packet_t *packet = (queued_packet_t *)malloc(sizeof(queued_packet_t) + size);
packet->identifier = THIS->m_packetIdentifier;
// If the stream didn't provide bitRate (m_bitRate == 0), then let's calculate it
if (THIS->m_bitRate == 0 && THIS->m_bitrateBufferIndex < kAudioStreamBitrateBufferSize) {
// Only keep sampling for one buffer cycle; this is to keep the counters (for instance) duration
// stable.
THIS->m_bitrateBuffer[THIS->m_bitrateBufferIndex++] = 8 * inPacketDescriptions[i].mDataByteSize / THIS->m_packetDuration;
if (THIS->m_bitrateBufferIndex == kAudioStreamBitrateBufferSize) {
if (THIS->m_delegate) {
THIS->m_delegate->bitrateAvailable();
}
}
}
AS_LOCK_TRACE("streamDataCallback: lock\n");
pthread_mutex_trylock(&THIS->m_packetQueueMutex);
/* Prepare the packet */
packet->next = NULL;
packet->desc = inPacketDescriptions[i];
packet->desc.mStartOffset = 0;
memcpy(packet->data, (const char *)inInputData + inPacketDescriptions[i].mStartOffset,
size);
if (THIS->m_queuedHead == NULL) {
THIS->m_queuedHead = THIS->m_queuedTail = THIS->m_playPacket = packet;
} else {
THIS->m_queuedTail->next = packet;
THIS->m_queuedTail = packet;
}
THIS->m_cachedDataSize += size;
THIS->m_packetIdentifier++;
AS_LOCK_TRACE("streamDataCallback: unlock\n");
pthread_mutex_unlock(&THIS->m_packetQueueMutex);
}
THIS->determineBufferingLimits();
}
All the FreeStreamer project has been rewrite wieth Swift 3.0 in here FreePlayer
Answer can be found here AudioStream
Related
I'm currently trying to develop snmpManager in my embedded device(linux OS). This device should communicate with another device's snmpAgent. Well, my code working good for snmpV2c also my snmp packets show in wireshark pcap. But when i try snmpV3 packets snmp_synch_response(session, requestPdu, &responsePdu) func return STAT_ERROR Btw snmpV2c was returning STAT_SUCCESS My code is shown in below, i created snmp session for v2 and v3.
snmp_session *snmptypes::fCreateSession(QString IP)
{
struct snmp_session session;
snmp_sess_init(&session);
session.peername = strdup(IP.toAscii());
static const char* v3_passphrase = "blablabl";
session.retries = 2;
session.timeout = 1000000;
session.engineBoots = 0;
session.engineTime = 0;
QString engineID = "0000000c000000007f000001";
session.contextEngineID = reinterpret_cast<uchar*>(strdup(engineID.toAscii()));
qDebug()<<"contextEngineID:"<<session.contextEngineID;
/*memory allocated by strdup() will be freed by calling snmp_close() */
if(SnmpMainWidget::activeWaveForm == SnmpMainWidget::snmpv2c)
{
session.version = SNMP_VERSION_2c;
const char *str;
QString path = "user";
QByteArray ba;
ba = path.toLatin1();
str = ba.data();
session.community = const_cast<unsigned char*>(reinterpret_cast<const unsigned char *>(str));
session.community_len = path.length();
session.retries = 2;
session.timeout = 1000000;
}
else if (SnmpMainWidget::activeWaveForm == SnmpMainWidget::snmpv3)
{
session.version = SNMP_VERSION_3;
session.securityName = strdup("blabl");
session.securityNameLen = strlen(session.securityName);
session.securityModel = USM_SEC_MODEL_NUMBER;
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
// session.securityAuthProtoLen = sizeof (usmHMACMD5AuthProtocol) / sizeof (oid);
session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
session.securityPrivProto = usmNoPrivProtocol;
session.securityPrivProtoLen = USM_PRIV_PROTO_NOPRIV_LEN;
session.securityAuthKeyLen =USM_AUTH_KU_LEN;
if (generate_Ku(session.securityAuthProto,session.securityAuthProtoLen,(u_char *) (v3_passphrase),
strlen(v3_passphrase),session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS)
{
qWarning()<<"ERROR KODU"<<generate_Ku(session.securityAuthProto,session.securityAuthProtoLen,(u_char *) (v3_passphrase),
strlen(v3_passphrase),session.securityAuthKey,
&session.securityAuthKeyLen);
snmp_log(LOG_ERR,"Error generating Ku from authentication pass phrase. \n");
exit(1);
}
}
struct snmp_session* openedSession = snmp_open(&session);
if(openedSession == nullptr)
{
snmp_sess_perror(reinterpret_cast<const char*>("No Ack!"), openedSession);
qWarning()<<"Error while Session opening! FILE:"<<__FILE__<<"LINE:"<<__LINE__;
}
else
qDebug()<<"creating session is succeed!";
return openedSession;
}
Session is creating successfully. no problem so far. Let's continue, then i create pdu like shown below,
snmp_pdu *snmptypes::fCreatePDU(tsPDUvariables PDUvariables)
{
// qDebug()<<"nrepeaters"<<PDUvariables.nrepeaters;
// qDebug()<<"mrepeaters"<<PDUvariables.mrepetitions;
qDebug()<<"setParam"<<PDUvariables.setParam;
qDebug()<<"dataType"<<PDUvariables.dataType;
struct snmp_pdu* requestPdu;
oid requestOid[MAX_OID_LEN];
size_t requestOidLength = MAX_OID_LEN;
snmp_parse_oid(static_cast<const char*>(PDUvariables.OID.toAscii()), requestOid, &requestOidLength);
switch (PDUvariables.msgType) {
case snmpGet:
requestPdu = snmp_pdu_create(SNMP_MSG_GET);
if (SnmpMainWidget::activeWaveForm == SnmpMainWidget::snmpv3){
requestPdu->version = SNMP_VERSION_3;
requestPdu->securityName = strdup("user");
requestPdu->securityNameLen = strlen(requestPdu->securityName);
requestPdu->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
requestPdu->securityModel = USM_SEC_MODEL_NUMBER;
}
snmp_add_null_var(requestPdu, requestOid, requestOidLength);
break;
//there are some other case like set,getbulk but don't need to focus these.get enough for now.
}
qDebug()<<"creating PDU is succeed!";
return requestPdu;
}
Finally I'm using snmpget func. for send snmp packet to dedicated IP;
Problem occure right here, snmpv2 snmpStatus return success but snmpv3 reutn error. There for snmpv3 packet doesn't appear on wireshark and also doesn't send. I cant understand what am i wrong in V3 while V2 was working well.
bool snmptypes::fSnmpGet(QString IP,QString OID)
{
const char PACKET_HEADER_BYTE = 4;//4Byte = SOH + len/256 + len%256 + EOH
struct snmp_session* session = fCreateSession(IP);
tsPDUvariables PDUvariables;
PDUvariables.OID = OID;
PDUvariables.msgType = snmpGet;
struct snmp_pdu* requestPdu = fCreatePDU(PDUvariables);
qDebug()<<"PDUvariables.msgType"<<PDUvariables.msgType;
qDebug()<<"PDUvariables.dataType"<<PDUvariables.dataType;
qDebug()<<"PDUvariables.OID"<<PDUvariables.OID;
qDebug()<<"PDUvariables.setParam"<<PDUvariables.setParam;
struct snmp_pdu* responsePdu = nullptr;
if(requestPdu != nullptr && session != nullptr)
{
int snmpStatus = snmp_synch_response(session, requestPdu, &responsePdu);
qDebug()<<"snmpStatus"<<snmpStatus;
qDebug()<<"requestStatus::errStat"<<requestPdu->errstat;
qDebug()<<"session->s_errno"<<session->s_errno;
if(snmpStatus == STAT_SUCCESS)
{
if( responsePdu->errstat == SNMP_ERR_NOERROR and responsePdu->errstat == SNMP_ERR_NOERROR)
{
/* SUCCESS: Print the result variables */
struct variable_list *snmpVariables;
for(snmpVariables = responsePdu->variables; snmpVariables; snmpVariables = snmpVariables->next_variable)
{ qDebug()<<"fSnmpGet"<<__LINE__;
print_variable(snmpVariables->name, snmpVariables->name_length, snmpVariables);
}
for(snmpVariables = responsePdu->variables; snmpVariables != nullptr; snmpVariables = snmpVariables->next_variable)
{
QString strOID;
for(size_t i =0; i<snmpVariables->name_length;i++)
{
qDebug()<<"snmpVariables->name[i]"<<snmpVariables->name[i];
strOID.append(".");
strOID.append(QString::number( snmpVariables->name[i]));
}
qDebug()<<"strOID"<<strOID;
unsigned int OIDLen = static_cast<unsigned int> (strOID.length());
qDebug()<<"OID_Length:"<< static_cast<unsigned int> (OIDLen);
unsigned int paramLen = static_cast<unsigned int> (snmpVariables->val_len);
qDebug()<<"paramLen"<<paramLen;
unsigned int txDataLen = 5+OIDLen + paramLen;
qDebug()<<"txDataLen"<<txDataLen;
int totalTxDataLen = txDataLen+PACKET_HEADER_BYTE;
unsigned char *outMessBuff = static_cast<unsigned char *>(malloc(totalTxDataLen));
unsigned char *dataBuff = static_cast<unsigned char *>(malloc(txDataLen));
dataBuff[0] = snmpReqResponse;
dataBuff[1] = static_cast<unsigned char>(OIDLen/256 );
dataBuff[2] = static_cast<unsigned char>(OIDLen%256) ;
dataBuff[3] = static_cast<unsigned char>(paramLen/256) ;
dataBuff[4] = static_cast<unsigned char>(paramLen%256 );
if(OIDLen > 0)
{
memcpy(dataBuff+5,strOID.toAscii(),OIDLen);
}
if(paramLen > 0)
{
memcpy(dataBuff+5+OIDLen,snmpVariables->val.string,paramLen);
}
totalTxDataLen = SnmpMainWidget::IPC.prepareIPCmessage(outMessBuff, dataBuff, static_cast<int>(txDataLen));
SnmpMainWidget::IPC.sendIpcMessageToQt(reinterpret_cast<const char *>(outMessBuff),static_cast<unsigned int>(totalTxDataLen)); // reinterpret_cast bit of dangerous should be good test !!
SnmpMainWidget::IPC.PrintPacketInHex("SNMP-->QT :",outMessBuff, static_cast<unsigned int>(totalTxDataLen));
free(dataBuff);
free(outMessBuff);
}
}
}
else
{
if(snmpStatus == STAT_SUCCESS)
{
fprintf(stderr, "Error in packet\nReason: %s\n", snmp_errstring(responsePdu->errstat));
}
else if(snmpStatus == STAT_TIMEOUT)
{
fprintf(stderr, "Timeout: No response from %s.\n", session->peername);
}
else
{
printf("snmp get requestPdu->command: %d\n", requestPdu->command);
printf("snmp get errstat: %s\n", snmp_errstring(requestPdu->errstat));
printf("snmp get errindex: %s\n", snmp_errstring(requestPdu->errindex));
fprintf(stderr, "session->s_errno %d,session->s_snmp_errno %d \n", session->s_errno, session->s_snmp_errno);
}
if(responsePdu)
{
snmp_free_pdu(responsePdu);
}
snmp_close(session);
}
}
else
return false;
return true;
}
I'm just solving the problem. When I checked the internet I realize missing init_snmp("snmpapp"); function in my code. After adding init snmp, v3 set get message is send perfectly.
snmp_session *snmptypes::fCreateSession(QString IP)
{
struct snmp_session session;
init_snmp("snmpapp");
snmp_sess_init(&session);
session.peername = strdup(IP.toAscii());
.....
}
Maybe it would be a useful answer for people who will have the same problem.
Environment:
Ubuntu 16.04 (x64)
C++
ffmpeg
Use-case
Multiple MPEG-TS fragments are rapidly decoded ( numerous every sec )
The format of the TS fragments is dynamic and can't be known ahead of time
The first A/V frames of each fragment are needed to be extracted
Problem statement
The code bellow successfully decodes A/V, BUT, has a huge memory leak ( MBytes/sec )
According to the docs seems all memory is freed as it should ( does it... ? )
Why do I get this huge mem leak, what am I missing in the following code snap ?
struct MEDIA_TYPE {
ffmpeg::AVMediaType eType;
union {
struct {
ffmpeg::AVPixelFormat colorspace;
int width, height;
float fFPS;
} video;
struct : WAVEFORMATEX {
short sSampleFormat;
} audio;
} format;
};
struct FRAME {
enum { MAX_PALNES = 3 + 1 };
int iStrmId;
int64_t pts; // Duration in 90Khz clock resolution
uint8_t** ppData; // Null terminated
int32_t* pStride;// Zero terminated
};
HRESULT ProcessTS(IN Operation op, IN uint8_t* pTS, IN uint32_t uiBytes, bool(*cb)(IN const MEDIA_TYPE& mt, IN FRAME& frame, IN PVOID pCtx), IN PVOID pCbCtx)
{
uiBytes -= uiBytes % 188;// align to 188 packet size
struct CONTEXT {
uint8_t* pTS;
uint32_t uiBytes;
int32_t iPos;
} ctx = { pTS, uiBytes, 0 };
LOGTRACE(TSDecoder, "ProcessTS(%d, 0x%.8x, %d, 0x%.8x, 0x%.8x), this=0x%.8x\r\n", (int)op, pTS, uiBytes, cb, pCbCtx, this);
ffmpeg::AVFormatContext* pFmtCtx = 0;
if (0 == (pFmtCtx = ffmpeg::avformat_alloc_context()))
return E_OUTOFMEMORY;
ffmpeg::AVIOContext* pIoCtx = ffmpeg::avio_alloc_context(pTS, uiBytes, 0, &ctx
, [](void *opaque, uint8_t *buf, int buf_size)->int {
auto pCtx = (CONTEXT*)opaque;
int size = pCtx->uiBytes;
if (pCtx->uiBytes - pCtx->iPos < buf_size)
size = pCtx->uiBytes - pCtx->iPos;
if (size > 0) {
memcpy(buf, pCtx->pTS + pCtx->iPos, size);
pCtx->iPos += size;
}
return size;
}
, 0
, [](void* opaque, int64_t offset, int whence)->int64_t {
auto pCtx = (CONTEXT*)opaque;
switch (whence)
{
case SEEK_SET:
pCtx->iPos = offset;
break;
case SEEK_CUR:
pCtx->iPos += offset;
break;
case SEEK_END:
pCtx->iPos = pCtx->uiBytes - offset;
break;
case AVSEEK_SIZE:
return pCtx->uiBytes;
}
return pCtx->iPos;
});
pFmtCtx->pb = pIoCtx;
int iRet = ffmpeg::avformat_open_input(&pFmtCtx, "fakevideo.ts", m_pInputFmt, 0);
if (ERROR_SUCCESS != iRet) {
assert(false);
pFmtCtx = 0;// a user-supplied AVFormatContext will be freed on failure.
return E_FAIL;
}
struct DecodeContext {
ffmpeg::AVStream* pStream;
ffmpeg::AVCodec* pDecoder;
int iFramesProcessed;
};
HRESULT hr = S_OK;
int iStreamsProcessed = 0;
bool bVideoFound = false;
int64_t ptsLast = 0;
int64_t dtsLast = 0;
auto pContext = (DecodeContext*)alloca(sizeof(DecodeContext) * pFmtCtx->nb_streams);
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++) {
assert(pFmtCtx->streams[i]->index == i);
pContext[i].pStream = pFmtCtx->streams[i];
pContext[i].pDecoder = ffmpeg::avcodec_find_decoder(pFmtCtx->streams[i]->codec->codec_id);
pContext[i].iFramesProcessed= 0;
if (0 == pContext[i].pDecoder)
continue;
if ((iRet = ffmpeg::avcodec_open2(pFmtCtx->streams[i]->codec, pContext[i].pDecoder, NULL)) < 0) {
_ASSERT(FALSE);
hr = E_FAIL;
goto ErrExit;
}
}
while (S_OK == hr) {
ffmpeg::AVFrame* pFrame = 0;
ffmpeg::AVPacket pkt;
ffmpeg::av_init_packet(&pkt);
if (ERROR_SUCCESS != (iRet = ffmpeg::av_read_frame(pFmtCtx, &pkt))) {
hr = E_FAIL;
break;
}
if ((0 == dtsLast) && (0 != pkt.dts))
dtsLast = pkt.dts;
if ((0 == ptsLast) && (0 != pkt.pts))
ptsLast = pkt.pts;
DecodeContext& ctx = pContext[pkt.stream_index];
if (Operation::DECODE_FIRST_FRAME_OF_EACH_STREAM == op) {
if (iStreamsProcessed == pFmtCtx->nb_streams) {
hr = S_FALSE;
goto Next;
}
if (ctx.iFramesProcessed > 0)
goto Next;
iStreamsProcessed++;
}
if (0 == ctx.pDecoder)
goto Next;
if (0 == (pFrame = ffmpeg::av_frame_alloc())) {
hr = E_OUTOFMEMORY;
goto Next;
}
LOGTRACE(TSDecoder, "ProcessTS(%d, 0x%.8x, %d, 0x%.8x, 0x%.8x), this=0x%.8x, decode, S:%d, T:%d\r\n", (int)op, pTS, uiBytes, cb, pCbCtx, this, pkt.stream_index, ctx.pStream->codec->codec_type);
int bGotFrame = false;
int iBytesUsed = 0;
MEDIA_TYPE mt;
memset(&mt, 0, sizeof(mt));
mt.eType = ctx.pStream->codec->codec_type;
switch (mt.eType) {
case ffmpeg::AVMediaType::AVMEDIA_TYPE_AUDIO:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if((iRet = ffmpeg::avcodec_decode_audio4(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
goto Next;
}
_ASSERT(pkt.size == iRet);
// FFMPEG AAC decoder oddity, first call to 'avcodec_decode_audio4' results mute audio where the second result the expected audio
bGotFrame = false;
if ((iRet = ffmpeg::avcodec_decode_audio4(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
goto Next;
}
_ASSERT(pkt.size == iRet);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (false == bGotFrame)
goto Next;
iBytesUsed = ctx.pStream->codec->frame_size;
mt.format.audio.nChannels = ctx.pStream->codec->channels;
mt.format.audio.nSamplesPerSec = ctx.pStream->codec->sample_rate;
mt.format.audio.wBitsPerSample = ffmpeg::av_get_bytes_per_sample(ctx.pStream->codec->sample_fmt) * 8;
mt.format.audio.nBlockAlign = mt.format.audio.nChannels * mt.format.audio.wBitsPerSample / 8;
mt.format.audio.sSampleFormat = (short)pFrame->format;
break;
case ffmpeg::AVMediaType::AVMEDIA_TYPE_VIDEO:
if ((iRet = ffmpeg::avcodec_decode_video2(ctx.pStream->codec, pFrame, &bGotFrame, &pkt)) < 0) {
hr = E_FAIL;
break;
}
if (false == bGotFrame)
goto Next;
assert(ffmpeg::AVPixelFormat::AV_PIX_FMT_YUV420P == ctx.pStream->codec->pix_fmt);// Thats is the only color space currently supported
iBytesUsed = (ctx.pStream->codec->width * ctx.pStream->codec->height * 3) / 2;
mt.format.video.width = ctx.pStream->codec->width;
mt.format.video.height = ctx.pStream->codec->height;
mt.format.video.colorspace = ctx.pStream->codec->pix_fmt;
mt.format.video.fFPS = (float)ctx.pStream->codec->framerate.num / ctx.pStream->codec->framerate.den;
bVideoFound = true;
break;
default:
goto Next;
}
ctx.iFramesProcessed++;
{
FRAME f = { ctx.pStream->index, ((0 == ptsLast) ? dtsLast : ptsLast), (uint8_t**)pFrame->data, (int32_t*)pFrame->linesize };
if ((iRet > 0) && (false == cb(mt, f, pCbCtx)))
hr = S_FALSE;// Breaks the loop
}
Next:
ffmpeg::av_free_packet(&pkt);
if (0 != pFrame) {
//ffmpeg::av_frame_unref(pFrame);
ffmpeg::av_frame_free(&pFrame);
pFrame = 0;
}
}
ErrExit:
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++)
ffmpeg::avcodec_close(pFmtCtx->streams[i]->codec);
pIoCtx->buffer = 0;// We have allocated the buffer, no need for ffmpeg to free it 4 us
pFmtCtx->pb = 0;
ffmpeg::av_free(pIoCtx);
ffmpeg::avformat_close_input(&pFmtCtx);
ffmpeg::avformat_free_context(pFmtCtx);
return hr;
}
You need to unref the packets before reusing them. And there's no need to allocate and deallocate them all the time.
Here's how I do it which might help you:
// Initialise a packet queue
std::list<AVPacket *> packets;
...
for (int c = 0; c < MAX_PACKETS; c++) {
ff->packets.push_back(av_packet_alloc());
}
while (!quit) {
... get packet from queue
int err = av_read_frame(ff->context, packet);
... process packet (audio, video, etc)
av_packet_unref(packet); // add back to queue for reuse
}
// Release packets
while (ff->packets.size()) { // free packets
AVPacket *packet = ff->packets.front();
av_packet_free(&packet);
ff->packets.pop_front();
}
In your code you've freed a packet which wasn't allocated in the first place.
How do you copy one stream to another using dedicated read/write threads in C++?
Let's say I have these methods (not real, but to illustrate the point) to read/write data from. These read/write functions could represent anything (network/file/USB/serial/etc).
// returns the number of bytes read
void read(char* buffer, int bufferSize, int* bytesRead);
// returns the number of bytes written
void write(char* buffer, int bufferSize, int* bytesWritten);
The solution should also be portable.
NOTE: I am aware that Windows has a FILE_FLAG_OVERLAPPED feature, but this assumes that the read/write is file IO. Remember, these read/write methods could represent anything.
Here is the solution I came up with.
Header
#pragma once
#include <stdlib.h>
#include <queue>
#include <mutex>
#include <thread>
#include <chrono>
#include <list>
#include <thread>
#define ASYNC_COPY_READ_WRITE_SUCCESS 0
struct BufferBlock;
struct ReadStream
{
// read a stream to a buffer.
// return non-zero if error occured
virtual int read(char* buffer, int bufferSize, int* bytesRead) = 0;
};
struct WriteStream
{
// write a buffer to a stream.
// return non-zero if error occured
virtual int write(char* buffer, int bufferSize, int* bytesWritten) = 0;
};
class BufferBlockManager
{
public:
BufferBlockManager(int numberOfBlocks, int bufferSize);
~BufferBlockManager();
void enqueueBlockForRead(BufferBlock* block);
void dequeueBlockForRead(BufferBlock** block);
void enqueueBlockForWrite(BufferBlock* block);
void dequeueBlockForWrite(BufferBlock** block);
void resetState();
private:
std::list<BufferBlock*> blocks;
std::queue<BufferBlock*> blocksPendingRead;
std::queue<BufferBlock*> blocksPendingWrite;
std::mutex queueLock;
std::chrono::milliseconds dequeueSleepTime;
};
void AsyncCopyStream(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream, int* readResult, int* writeResult);
CPP
#include "AsyncReadWrite.h"
struct BufferBlock
{
BufferBlock(int bufferSize) : buffer(NULL)
{
this->bufferSize = bufferSize;
this->buffer = new char[bufferSize];
this->actualSize = 0;
this->isLastBlock = false;
}
~BufferBlock()
{
this->bufferSize = 0;
free(this->buffer);
this->buffer = NULL;
this->actualSize = 0;
}
char* buffer;
int bufferSize;
int actualSize;
bool isLastBlock;
};
BufferBlockManager::BufferBlockManager(int numberOfBlocks, int bufferSize)
{
dequeueSleepTime = std::chrono::milliseconds(100);
for (int x = 0; x < numberOfBlocks; x++)
{
BufferBlock* block = new BufferBlock(bufferSize);
blocks.push_front(block);
blocksPendingRead.push(block);
}
}
BufferBlockManager::~BufferBlockManager()
{
for (std::list<BufferBlock*>::const_iterator iterator = blocks.begin(), end = blocks.end(); iterator != end; ++iterator) {
delete (*iterator);
}
}
void BufferBlockManager::enqueueBlockForRead(BufferBlock* block)
{
queueLock.lock();
block->actualSize = 0;
block->isLastBlock = false;
blocksPendingRead.push(block);
queueLock.unlock();
}
void BufferBlockManager::dequeueBlockForRead(BufferBlock** block)
{
WAITFOR:
while (blocksPendingRead.size() == 0)
std::this_thread::sleep_for(dequeueSleepTime);
queueLock.lock();
if (blocksPendingRead.size() == 0)
{
queueLock.unlock();
goto WAITFOR;
}
*block = blocksPendingRead.front();
blocksPendingRead.pop();
queueLock.unlock();
}
void BufferBlockManager::enqueueBlockForWrite(BufferBlock* block)
{
queueLock.lock();
blocksPendingWrite.push(block);
queueLock.unlock();
}
void BufferBlockManager::dequeueBlockForWrite(BufferBlock** block)
{
WAITFOR:
while (blocksPendingWrite.size() == 0)
std::this_thread::sleep_for(dequeueSleepTime);
queueLock.lock();
if (blocksPendingWrite.size() == 0)
{
queueLock.unlock();
goto WAITFOR;
}
*block = blocksPendingWrite.front();
blocksPendingWrite.pop();
queueLock.unlock();
}
void BufferBlockManager::resetState()
{
queueLock.lock();
blocksPendingRead = std::queue<BufferBlock*>();
blocksPendingWrite = std::queue<BufferBlock*>();
for (std::list<BufferBlock*>::const_iterator iterator = blocks.begin(), end = blocks.end(); iterator != end; ++iterator) {
(*iterator)->actualSize = 0;
}
queueLock.unlock();
}
struct AsyncCopyContext
{
AsyncCopyContext(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream)
{
this->bufferBlockManager = bufferBlockManager;
this->readStream = readStream;
this->writeStream = writeStream;
this->readResult = ASYNC_COPY_READ_WRITE_SUCCESS;
this->writeResult = ASYNC_COPY_READ_WRITE_SUCCESS;
}
BufferBlockManager* bufferBlockManager;
ReadStream* readStream;
WriteStream* writeStream;
int readResult;
int writeResult;
};
void ReadStreamThread(AsyncCopyContext* asyncContext)
{
int bytesRead = 0;
BufferBlock* readBuffer = NULL;
int readResult = ASYNC_COPY_READ_WRITE_SUCCESS;
while (
// as long there hasn't been any write errors
asyncContext->writeResult == ASYNC_COPY_READ_WRITE_SUCCESS
// and we haven't had an error reading yet
&& readResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
// let's deque a block to read to!
asyncContext->bufferBlockManager->dequeueBlockForRead(&readBuffer);
readResult = asyncContext->readStream->read(readBuffer->buffer, readBuffer->bufferSize, &bytesRead);
readBuffer->actualSize = bytesRead;
readBuffer->isLastBlock = bytesRead == 0;
if (readResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
// this was a valid read, go ahead and queue it for writing
asyncContext->bufferBlockManager->enqueueBlockForWrite(readBuffer);
}
else
{
// an error occured reading
asyncContext->readResult = readResult;
// since an error occured, lets queue an block to write indicatiting we are done and there are no more bytes to read
readBuffer->isLastBlock = true;
readBuffer->actualSize = 0;
asyncContext->bufferBlockManager->enqueueBlockForWrite(readBuffer);
}
if (readBuffer->isLastBlock) return;
}
}
void WriteStreamThread(AsyncCopyContext* asyncContext)
{
int bytesWritten = 0;
BufferBlock* writeBuffer = NULL;
int writeResult = ASYNC_COPY_READ_WRITE_SUCCESS;
bool isLastWriteBlock = false;
while (
// as long as there are no errors during reading
asyncContext->readResult == ASYNC_COPY_READ_WRITE_SUCCESS
// and we haven't had an error writing yet
&& writeResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
// lets dequeue a block for writing!
asyncContext->bufferBlockManager->dequeueBlockForWrite(&writeBuffer);
isLastWriteBlock = writeBuffer->isLastBlock;
if (writeBuffer->actualSize > 0)
writeResult = asyncContext->writeStream->write(writeBuffer->buffer, writeBuffer->actualSize, &bytesWritten);
if (writeResult == ASYNC_COPY_READ_WRITE_SUCCESS)
{
asyncContext->bufferBlockManager->enqueueBlockForRead(writeBuffer);
if (isLastWriteBlock) return;
}
else
{
asyncContext->writeResult = writeResult;
asyncContext->bufferBlockManager->enqueueBlockForRead(writeBuffer);
return;
}
}
}
void AsyncCopyStream(BufferBlockManager* bufferBlockManager, ReadStream* readStream, WriteStream* writeStream, int* readResult, int* writeResult)
{
AsyncCopyContext asyncContext(bufferBlockManager, readStream, writeStream);
std::thread readThread(ReadStreamThread, &asyncContext);
std::thread writeThread(WriteStreamThread, &asyncContext);
readThread.join();
writeThread.join();
*readResult = asyncContext.readResult;
*writeResult = asyncContext.writeResult;
}
Usage
#include <stdio.h>
#include <tchar.h>
#include "AsyncReadWrite.h"
struct ReadTestStream : ReadStream
{
int readCount = 0;
int read(char* buffer, int bufferSize, int* bytesRead)
{
printf("Starting read...\n");
memset(buffer, bufferSize, 0);
if (readCount == 10)
{
*bytesRead = 0;
return 0;
}
// pretend this function takes a while!
std::this_thread::sleep_for(std::chrono::milliseconds(100));
char buff[100];
sprintf_s(buff, "This is read number %d\n", readCount);
strcpy_s(buffer, sizeof(buff), buff);
*bytesRead = strlen(buffer);
readCount++;
printf("Finished read...\n");
return 0;
}
};
struct WriteTestStream : WriteStream
{
int write(char* buffer, int bufferSize, int* bytesWritten)
{
printf("Starting write...\n");
// pretend this function takes a while!
std::this_thread::sleep_for(std::chrono::milliseconds(500));
printf(buffer);
printf("Finished write...\n");
return 0;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
BufferBlockManager bufferBlockManager(5, 4096);
ReadTestStream readStream;
WriteTestStream writeStream;
int readResult = 0;
int writeResult = 0;
printf("Starting copy...\n");
AsyncCopyStream(&bufferBlockManager, &readStream, &writeStream, &readResult, &writeResult);
printf("Finished copy... readResult=%d writeResult=%d \n", readResult, writeResult);
getchar();
return 0;
}
EDIT: I put my solution into a GitHub repository here. If you wish to use this code, refer to the repository since it may be more updated than this answer.
Typically, you would just have one thread for each direction that alternates between reads and writes.
I have a collection of jpeg, which must be decoded by lib jpeg, and after it, encoded by x264 (after it encoded packets are streamed via rtmp).
Code I used for decoding:
struct my_error_mgr
{
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef my_error_mgr *my_error_ptr;
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
void init_source(j_decompress_ptr ptr)
{
Q_UNUSED(ptr)
}
boolean fill_input_buffer(j_decompress_ptr ptr)
{
Q_UNUSED(ptr)
return TRUE;
}
void term_source(j_decompress_ptr ptr)
{
Q_UNUSED(ptr)
}
void skip_input_data(j_decompress_ptr ptr, long num_bytes)
{
if(num_bytes>0)
{
ptr->src->next_input_byte+=(size_t)num_bytes;
ptr->src->bytes_in_buffer-=(size_t)num_bytes;
}
}
EtherDecoder::EtherDecoder(QObject *parent):
QObject(parent)
{
}
void EtherDecoder::dataBlockReady(QByteArray data)
{
jpeg_decompress_struct decompressInfo;
jpeg_create_decompress(&decompressInfo);
my_error_mgr err;
decompressInfo.do_fancy_upsampling = FALSE;
decompressInfo.src = (jpeg_source_mgr *) (*decompressInfo.mem->alloc_small) ((j_common_ptr) &decompressInfo, JPOOL_PERMANENT, sizeof(jpeg_source_mgr));
decompressInfo.err = jpeg_std_error(&err.pub);
err.pub.error_exit = my_error_exit;
if (setjmp(err.setjmp_buffer))
{
jpeg_destroy_decompress(&decompressInfo);
return;
}
decompressInfo.src->init_source = init_source;
decompressInfo.src->resync_to_restart = jpeg_resync_to_restart;
decompressInfo.src->fill_input_buffer = fill_input_buffer;
decompressInfo.src->skip_input_data = skip_input_data;
decompressInfo.src->term_source = term_source;
decompressInfo.src->next_input_byte = reinterpret_cast<const JOCTET*>(data.data());
decompressInfo.src->bytes_in_buffer = data.size();
jpeg_read_header(&decompressInfo, TRUE);
jpeg_start_decompress(&decompressInfo);
int size = 0;
int n_samples = 0;
char *samples = new char[5242880];
char *reserv = samples;
while (decompressInfo.output_scanline < decompressInfo.output_height)
{
n_samples = jpeg_read_scanlines(&decompressInfo, (JSAMPARRAY) &samples, 1);
samples += n_samples * decompressInfo.image_width * decompressInfo.num_components;
size += n_samples * decompressInfo.image_width * decompressInfo.num_components;
}
jpeg_finish_decompress(&decompressInfo);
QByteArray output(reserv, size);
emit frameReady(output, decompressInfo.output_width, decompressInfo.output_height);
jpeg_destroy_decompress(&decompressInfo);
delete[] reserv;
}
When I emit frameReady signal, I send data to Encoder, method, where I init Encedor looks like:
bool EtherEncoder::initEncoder(unsigned int width, unsigned int height)
{
x264_param_t param;
x264_param_default_preset(¶m, "veryfast", "zerolatency");
param.i_width=width;
param.i_height=height;
param.i_frame_total=0;
param.i_csp=X264_CSP_I420;
param.i_timebase_num=1;
param.i_timebase_den=96000;
param.b_annexb=true;
param.b_repeat_headers=false;
x264_param_apply_fastfirstpass(¶m);
x264_param_apply_profile(¶m, "baseline");
_context=x264_encoder_open(¶m);
if(!_context)
return false;
int nal_count;
x264_nal_t *nals;
if(x264_encoder_headers(_context, &nals, &nal_count)<0)
{
x264_encoder_close(_context);
_context=0;
return false;
}
_extradata=QByteArray();
_width=width;
_height=height;
if(nal_count>0)
{
_extradata=QByteArray(
(const char *)nals[0].p_payload,
nals[nal_count-1].p_payload+nals[nal_count-1].i_payload-nals[0].p_payload);
}
return true;
}
And encoding method:
void EtherEncoder::onFrameReady(QByteArray data, int width, int height)
{
while(data.size()>0)
{
if(!_context && initEncoder(width, height))
{
_timestampDelta=realTimestamp();
}
if(_context)
{
x264_picture_t pic;
x264_picture_init(&pic);
pic.i_type=X264_TYPE_AUTO;
pic.i_pts=_timestampDelta*96000;
pic.img.i_csp=X264_CSP_I420;
pic.img.i_plane=3;
int planeSize = width*height;
uint8_t *p = (uint8_t*)data.data();
pic.img.plane[0]=p;
p+=planeSize;
pic.img.plane[1]=p;
p+=planeSize/4;
pic.img.plane[2]=p;
pic.img.i_stride[0]=width;
pic.img.i_stride[1]=width/2;
pic.img.i_stride[2]=width/2;
if(_forceKeyFrame)
{
pic.i_type=X264_TYPE_I;
_forceKeyFrame=false;
}
int nal_count;
x264_nal_t *nals;
int rc=x264_encoder_encode(_context, &nals, &nal_count, &pic, &pic);
if(rc>0)
{
_mutex.lock();
_packets.push_back(
Packet(
QByteArray(
(const char *)nals[0].p_payload, nals[nal_count- 1].p_payload+nals[nal_count-1].i_payload-nals[0].p_payload),
_timestampDelta/96.0,
_timestampDelta/96.0,
pic.b_keyframe));
_timestampDelta+=40;
data.clear();
_mutex.unlock();
emit onPacketReady();
}
}
}
}
Decoding and encoding proceeds without errors, at the end I get valid video stream, but, it seems that in one of this steps I set Invalid data for decoder/encoder. I get only 1/4 part of image (top-left, as I understood) and it has invalid color and come color stripes. Maybe I set invalid strides and planes when encode frame, or maybe my setting data for libjpeg decoder is incorrect.. Please ask questions about my code, I'll try to make some explanations for you. I explodes my brain.. Thank you.
I am searching how to open archive from memory buffer using minizip.
I found ioapi_mem_c.zip from their page http://www.winimage.com/zLibDll/minizip.html.
I can't understand how to use it. Can some one give me an example?
I solved my problem for unziping:
I added this function to unzip.c
extern unzFile ZEXPORT unzOpenBuffer (const void* buffer, uLong size)
{
char path[16] = {0};
zlib_filefunc64_32_def memory_file;
uLong base = (uLong)buffer;
sprintf(path, "%x+%x", base, size);
fill_memory_filefunc64_32(&memory_file);
return unzOpenInternal(path, &memory_file, 0);
}
And made some changes in ioapi_mem.c:
void fill_memory_filefunc64_32 (pzlib_filefunc_def)
zlib_filefunc64_32_def* pzlib_filefunc_def;
{
pzlib_filefunc_def->zopen32_file = fopen_mem_func;
pzlib_filefunc_def->zfile_func64.zopen64_file = fopen_mem_func;
pzlib_filefunc_def->zfile_func64.zread_file = fread_mem_func;
pzlib_filefunc_def->zfile_func64.zwrite_file = fwrite_mem_func;
pzlib_filefunc_def->ztell32_file = ftell_mem_func;
pzlib_filefunc_def->zseek32_file = fseek_mem_func;
pzlib_filefunc_def->zfile_func64.zseek64_file = NULL;
pzlib_filefunc_def->zfile_func64.zclose_file = fclose_mem_func;
pzlib_filefunc_def->zfile_func64.zerror_file = ferror_mem_func;
pzlib_filefunc_def->zfile_func64.opaque = NULL;
}
I hope this will help someone who will have the same problem.
And Here is simple class implementation how to use it:
void ZipArchiveImpl::OpenArchive()
{
ASSERT(!mInited, "Already opened.");
if ((mFileMode == FM_READ))
{
if (mArchiveName.size() == 0)
{
u32 sz = mFile->GetFileSize();
mUnzBlock.Resize(sz);
mFile->SetPosition(0);
mFile->Read(mUnzBlock.Data(), mUnzBlock.GetSizeInBytes());
mUnzHandle = unzOpenBuffer(mUnzBlock.Data(), mUnzBlock.GetSizeInBytes());
}
else
{
mUnzHandle = unzOpen(mArchiveName.c_str());
}
if (!mUnzHandle) return;
FillMap();
}
else if (mFileMode == FM_WRITE)
{
ASSERT0(mArchiveName.size());
mZipHandle = zipOpen(mArchiveName.c_str(), 0);
if (!mZipHandle) return;
}
mInited = true;
}
IFile* ZipArchiveImpl::OpenRead(const std::string& name)
{
if (IsExist(name))
{
ZipFileInfo info = mFileMap.find(name)->second;
MemoryBlock block(1);
block.Resize(info.uncompressedSize);
int res = unzGoToFilePos(mUnzHandle, &info.filePosInfo);
if (UNZ_OK != res)
{
return false;
}
// open the current file with optional password
if (mArchivePassword != "")
{
res = unzOpenCurrentFilePassword(info.zipFileHandle, mArchivePassword.c_str());
}
else
{
res = unzOpenCurrentFile(info.zipFileHandle);
}
if (UNZ_OK != res)
{
return false;
}
// read uncompressed data
int readResult = unzReadCurrentFile(info.zipFileHandle, block.Data(), info.uncompressedSize);
// close the file
res = unzCloseCurrentFile(info.zipFileHandle);
if (UNZ_OK != res)
{
return false;
}
if (info.uncompressedSize == readResult)
{
return ROBE_NEW MemoryFile(block.Data(), info.uncompressedSize);
}
else
{
return NULL;
}
}
else
{
return NULL;
}
}
IFile* ZipArchiveImpl::OpenRead(u32 id)
{
ASSERT0(mFileNames.size() > id);
if (IsExist(mFileNames[id]))
{
return OpenRead(mFileNames[id]);
}
else
{
return NULL;
}
}
void ZipArchiveImpl::FillMap()
{
s32 walkRes = unzGoToFirstFile(mUnzHandle);
unz_file_info info;
while (UNZ_OK == walkRes)
{
// get info about current file
char currentFileName[512];
s32 fileInfoRes = unzGetCurrentFileInfo(mUnzHandle, &info, currentFileName, sizeof(currentFileName), 0, 0, 0, 0);
std::string name = std::string(currentFileName);
mFileNames.push_back(name);
InitInfo(name, &info);
walkRes = unzGoToNextFile(mUnzHandle);
}
OpenRead(0);
if (UNZ_END_OF_LIST_OF_FILE != walkRes)
{
}
}
void ZipArchiveImpl::InitInfo(const std::string& name, unz_file_info* info)
{
ZipFileInfo zfi;
mFileMap.insert(std::pair<std::string, ZipFileInfo>(name, zfi));
mFileMap[name].zipFileHandle = mUnzHandle;
int res = unzGetFilePos(mFileMap[name].zipFileHandle, &mFileMap[name].filePosInfo);
mFileMap[name].uncompressedSize = info->uncompressed_size;
char lastsymbol = name[name.size() - 1];
if (lastsymbol == '/' || lastsymbol == '\\')
{
mFileMap[name].type = ZFT_DIR;
}
else
{
mFileMap[name].type = ZFT_FILE;
}
}
ZipArchiveImpl::~ZipArchiveImpl()
{
if (mInited)
{
if (mUnzHandle) unzClose(mUnzHandle);
if (mZipHandle) zipClose(mZipHandle, 0);
}
}
bool ZipArchiveImpl::IsExist(const std::string& name)
{
return (mFileMap.find(name) != mFileMap.end());
}
void ZipArchiveImpl::SaveFileToZip(const std::string& path, IFile* file)
{
const u32 DefaultFileAttribute = 32;
MemoryBlock block(1);
block.Resize(file->GetFileSize());
file->Read(block.Data(), block.GetSizeInBytes());
zip_fileinfo zinfo;
memset(&zinfo, 0, sizeof(zinfo));
zinfo.internal_fa = 0;
zinfo.external_fa = DefaultFileAttribute;
::boost::posix_time::ptime pt = ::boost::posix_time::from_time_t(time(0));
std::tm ptm = ::boost::posix_time::to_tm(pt);
zinfo.dosDate = 0;
zinfo.tmz_date.tm_year = ptm.tm_year;
zinfo.tmz_date.tm_mon = ptm.tm_mon;
zinfo.tmz_date.tm_mday = ptm.tm_mday;
zinfo.tmz_date.tm_hour = ptm.tm_hour;
zinfo.tmz_date.tm_min = ptm.tm_min;
zinfo.tmz_date.tm_sec = ptm.tm_sec;
zipOpenNewFileInZip(mZipHandle, path.c_str(), &zinfo, 0, 0, 0, 0, 0, Z_DEFLATED, Z_BEST_SPEED);
zipWriteInFileInZip(mZipHandle, block.Data(), block.GetSizeInBytes());
zipCloseFileInZip(mZipHandle);
}
unsigned long ZipArchiveImpl::GetFileAttributes(const std::string& filePath)
{
unsigned long attrib = 0;
#ifdef WIN32
attrib = ::GetFileAttributes(filePath.c_str());
#else
struct stat path_stat;
if (::stat(filePath.c_str(), &path_stat) == 0)
{
attrib = path_stat.st_mode;
}
#endif
return attrib;
}
added this function to unzip.c
extern unzFile ZEXPORT unzOpenBuffer(const void* buffer, uLong size)
{
zlib_filefunc_def filefunc32 = { 0 };
ourmemory_t *punzmem = (ourmemory_t*)malloc(sizeof(ourmemory_t));
punzmem->size = size;
punzmem->base = (char *)malloc(punzmem->size);
memcpy(punzmem->base, buffer, punzmem->size);
punzmem->grow = 0;
punzmem->cur_offset = 0;
punzmem->limit = 0;
fill_memory_filefunc(&filefunc32, punzmem);
return unzOpen2(NULL, &filefunc32);
}
Looking at the code in the link, there is no obvious way to pass a memory buffer. Save your memory buffer as a file, unzip it.
Or you could implement your own zlib_filefunc_def variant that operates on a lump of memory instead of a file. I don't think that is very hard to do.
Checkout the nmoinvaz fork of minizip: To unzip from a zip file in memory use fill_memory_filefunc and supply a proper ourmemory_t structure
zlib_filefunc_def filefunc32 = {0};
ourmemory_t unzmem = {0};
unzmem.size = bufsize;
unzmem.base = (char *)malloc(unzmem.size);
memcpy(unzmem.base, buffer, unzmem.size);
fill_memory_filefunc(&filefunc32, &unzmem);
unzOpen2("__notused__", &filefunc32);