Commit ad69d2c5 authored by Sylvain Berfini's avatar Sylvain Berfini 🎩

Started rtt rework (use 2 filters for sending/receiving characters)

parent c04a7920
......@@ -146,7 +146,9 @@ typedef enum MSFilterId{
MS_MKV_PLAYER_ID,
MS_VAD_DTX_ID,
MS_BB10_DISPLAY_ID,
MS_BB10_CAPTURE_ID
MS_BB10_CAPTURE_ID,
MS_RTT_4103_SOURCE_ID,
MS_RTT_4103_SINK_ID
} MSFilterId;
......
......@@ -1172,30 +1172,13 @@ MS2_PUBLIC void audio_stream_set_audio_route(AudioStream *stream, MSAudioRoute r
* @{
**/
#define TS_OUTBUF_SIZE 32
#define TS_REDGEN 2
#define TS_NUMBER_OF_OUTBUF TS_REDGEN + 1
#define TS_INBUF_SIZE TS_OUTBUF_SIZE * TS_NUMBER_OF_OUTBUF
#define TS_KEEP_ALIVE_INTERVAL 25000 //10000
#define TS_SEND_INTERVAL 299
#define TS_FLAG_NOTFIRST 0x01
#define TS_FLAG_NOCALLBACK 0x02
struct _TextStream
{
MediaStream ms;
int flags;
MSFilter *rttsource;
MSFilter *rttsink;
int pt_t140;
int pt_red;
int prevseqno;
uint8_t inbuf[TS_INBUF_SIZE];
size_t inbufsize;
uint8_t* inbufpos;
uint8_t buf[TS_NUMBER_OF_OUTBUF][TS_OUTBUF_SIZE];
int pribuf;
size_t bufsize[TS_NUMBER_OF_OUTBUF];
uint32_t timestamp[TS_NUMBER_OF_OUTBUF];
int pt_red;
};
typedef struct _TextStream TextStream;
......@@ -1251,31 +1234,6 @@ MS2_PUBLIC void text_stream_stop (TextStream * stream);
*/
MS2_PUBLIC void text_stream_iterate(TextStream *stream);
/**
* Reads a character from the stream.
*
* @param[in] stream TextStream object previously created with text_stream_new().
* @return the character read or '\0' if there are no more character to read in the steam.
**/
MS2_PUBLIC char text_stream_getchar(TextStream *stream);
/**
* Writes a character to the stream.
* To write an utf8 character, just call it multiple times.
*
* @param[in] stream TextStream object previously created with text_stream_new().
* @param[in] c the Char to send.
**/
MS2_PUBLIC void text_stream_putchar(TextStream *stream, const char c);
/**
* Reads a character from the stream in UTF-32 format.
*
* @param[in] stream TextStream object previously created with text_stream_new().
* @return the character in UTF-32 format.
**/
MS2_PUBLIC uint32_t text_stream_getchar32(TextStream *stream);
/**
* Writes a character to stream in UTF-32 format.
*
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef msrtt4103_h
#define msrtt4103_h
#include <mediastreamer2/msfilter.h>
#define TS_FLAG_NOTFIRST 0x01
#define TS_FLAG_NOCALLBACK 0x02
#define TS_OUTBUF_SIZE 32
#define TS_REDGEN 2
#define TS_NUMBER_OF_OUTBUF TS_REDGEN + 1
#define TS_INBUF_SIZE TS_OUTBUF_SIZE * TS_NUMBER_OF_OUTBUF
#define TS_KEEP_ALIVE_INTERVAL 25000 //10000
#define TS_SEND_INTERVAL 299
#define MS_RTT_4103_SOURCE_SET_T140_PAYLOAD_TYPE_NUMBER MS_FILTER_METHOD(MS_RTT_4103_SOURCE_ID, 0, int)
#define MS_RTT_4103_SINK_SET_T140_PAYLOAD_TYPE_NUMBER MS_FILTER_METHOD(MS_RTT_4103_SINK_ID, 0, int)
#define MS_RTT_4103_SOURCE_SET_RED_PAYLOAD_TYPE_NUMBER MS_FILTER_METHOD(MS_RTT_4103_SOURCE_ID, 1, int)
#define MS_RTT_4103_SINK_SET_RED_PAYLOAD_TYPE_NUMBER MS_FILTER_METHOD(MS_RTT_4103_SINK_ID, 1, int)
#define MS_RTT_4103_SOURCE_PUT_CHAR32 MS_FILTER_METHOD(MS_RTT_4103_SOURCE_ID, 2, uint32_t)
typedef struct _RealtimeTextReceivedCharacter {
uint32_t character;
} RealtimeTextReceivedCharacter;
#define MS_RTT_4103_RECEIVED_CHAR MS_FILTER_EVENT(MS_RTT_4103_SINK_ID, 0, RealtimeTextReceivedCharacter)
#endif
......@@ -94,6 +94,8 @@ libmediastreamer_voip_la_SOURCES+= voip/private.h \
voip/audiostream.c \
voip/ringstream.c \
voip/rfc4103_textstream.c \
otherfilters/rfc4103_source.cpp \
otherfilters/rfc4103_sink.cpp \
voip/msmediaplayer.c \
voip/ice.c \
otherfilters/msrtp.c \
......
/*
* rfc4103_sink.cpp - Real time text RFC 4103 sender.
*
* Copyright (C) 2015 Belledonne Communications, Grenoble, France
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/msrtt4103.h"
typedef struct _RealTimeTextSinkData {
int flags;
int prevseqno;
uint8_t inbuf[TS_INBUF_SIZE];
size_t inbufsize;
uint8_t* inbufpos;
int pt_t140;
int pt_red;
} RealTimeTextSinkData;
/**
* How many bytes makes up one character.
**/
static int utf8_test(const uint8_t c) {
if (!(c & 0x80)) { return 1; }
else if (!(c & 0x40)) { return 0; }
else if (!(c & 0x20)) { return 2; }
else if (!(c & 0x10)) { return 3; }
else if (!(c & 0x08)) { return 4; }
else { return -1; }
}
static bool_t is_utf8_buf_ok(const uint8_t* b, const size_t s) {
int i, t;
for (i = 0, t = 0; i < (int)s; i++, t--) {
if (t == 0) {
t = utf8_test(b[i]);
if (t <= 0) {
return FALSE;
}
} else {
if (utf8_test(b[i]) != 0) {
return FALSE;
}
}
}
if (t) {
return FALSE;
}
return TRUE; /* SUCCESS */
}
static int red_needed(int cur, int prev) {
int t = cur-prev;
if (t > 0) {
return t - 1;
} else if (t < -100) {
return t + 0xFFFF;
}
return -1;
}
static void text_stream_hexdump(const uint8_t* data, const size_t s) {
char buf[1000];
const uint8_t* c = data;
int pos = 0;
int i = 0;
buf[0] = '\0';
while(c < data + s) {
pos += snprintf(&buf[pos], 1000 - pos, " ['%c']0x%X", *c > 20 ? *c : 'X', *c);
c++;
i++;
}
buf[pos] = '\0';
ms_debug("%s", buf);
}
static void insert_lost_char(uint8_t *p){
p[0] = 0xEF;
p[1] = 0xBF;
p[2] = 0xBD;
}
static int read_t140_data(RealTimeTextSinkData *stream, uint8_t *data, int readsize) {
int buf_size = (int)TS_INBUF_SIZE - stream->inbufsize;
if (readsize < 0) {
ms_warning("corrupt packet (readsize<0)");
return -1;
} else if (readsize > buf_size) {
readsize = buf_size;
ms_warning("reading less characters than in buffer");
/*POTENTIAL BUG... if last char is a multi char but just parts of it get
in, next function fails and whole buf is ignored. */
}
if (readsize > 0) {
ms_debug("Reading %i bytes\n", readsize);
if (!is_utf8_buf_ok(data, readsize)) {
text_stream_hexdump(data, readsize);
ms_warning("not a valid utf8 payload");
stream->inbufsize = 0;
return -1;
}
memcpy(&stream->inbuf[stream->inbufsize], data, readsize);
stream->inbufsize += readsize;
}
return 0; /* 0 on success */
}
static void process_t140_packet(RealTimeTextSinkData *stream, mblk_t *packet) {
int seqno = rtp_get_seqnumber(packet);
uint8_t *payload;
int payloadsize = rtp_get_payload(packet, &payload);
ms_debug("t140 seqno:%i",seqno);
if (stream->flags & TS_FLAG_NOTFIRST) {
int t = red_needed(seqno, stream->prevseqno);
if (t < 0) {
ms_warning("packet arrived out of order");
return;
} else if (t > 0) {
stream->inbufsize = 3;
insert_lost_char(stream->inbuf);
}
}
if (read_t140_data(stream, payload, payloadsize)) {
return; /* return without updatting seqno */
}
stream->prevseqno = seqno;
}
static void process_red_packet(RealTimeTextSinkData *stream, mblk_t *packet) {
int seqno = rtp_get_seqnumber(packet);
uint8_t *payload;
int redgen = 0;
int pos = 0;
int redneeded;
int readstart;
int payloadsize = rtp_get_payload(packet, &payload);
/* check how many is red, also check if its the right payload for the red */
ms_debug("red seqno:%i", seqno);
while ((pos < payloadsize) && (payload[pos] & (1 << 7))) {
redgen++;
if (((int)payload[pos] & 0x7F) != stream->pt_t140) {
ms_warning("invalid red packet");
return;
}
pos += 4;
}
ms_debug("red redgen:%i",redgen);
if ((int)payload[pos] != stream->pt_t140) {
ms_warning("invalid red packet");
return;
}
if (stream->flags & TS_FLAG_NOTFIRST) {
redneeded = red_needed(seqno, stream->prevseqno);
} else {
redneeded = 0;
}
if (redneeded < 0) {
ms_warning("packet arrived out of order");
return;
}
ms_debug("red redneeded:%i", redneeded);
if (redneeded > redgen) {
/* we need more red than we got */
stream->inbufsize = 3;
insert_lost_char(stream->inbuf);
redneeded = redgen;
}
/* loop over unneeded red */
readstart = redgen * 4 + 1;
for (pos = 0; pos < (redgen - redneeded) * 4; pos += 4) {
readstart += (((uint32_t)payload[pos + 2] << 8) | (uint32_t)payload[pos + 3]) & 0x3FF;
}
if (read_t140_data(stream, &payload[readstart], payloadsize - readstart)) {
ms_debug("error reading");
return; /* return without updating seqno */
}
stream->prevseqno = seqno;
}
static bool_t read_text_packet(RealTimeTextSinkData *stream, mblk_t *packet) {
int pt;
stream->inbufpos = stream->inbuf;
stream->inbufsize = 0;
if (packet == NULL) {
return FALSE;
}
pt = rtp_get_payload_type(packet);
if (pt == stream->pt_t140) {
process_t140_packet(stream, packet);
} else if (stream->pt_red && pt == stream->pt_red) {
process_red_packet(stream, packet);
} else {
ms_warning("unkown pt for text packet (pt:%i t140:%i red:%i)", pt, stream->pt_t140, stream->pt_red);
}
if (!(stream->flags & TS_FLAG_NOTFIRST)) {
stream->flags |= TS_FLAG_NOTFIRST;
}
freemsg(packet);
return TRUE;
}
static bool_t text_stream_ischar(RealTimeTextSinkData *stream) {
if (stream->inbufsize) {
return TRUE;
}
return FALSE;
}
static char text_stream_getchar(RealTimeTextSinkData *stream) {
uint8_t *p = stream->inbufpos;
uint8_t *end = &stream->inbuf[stream->inbufsize];
while (end > p) {
if (p[0] != '\0') {
if (end - p >= 3) {
if (p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF) { /* BOM */
p += 3;
continue;
}
}
stream->inbufpos = p + 1;
return *p;
}
p++;
}
return '\0';
}
static uint32_t text_stream_getchar32(RealTimeTextSinkData *stream) {
uint32_t c = text_stream_getchar(stream);
int t = utf8_test(c);
switch (t) {
case 1:
return c;
case 2:
c = (c & 0x1F) << 6;
c += ((uint32_t) text_stream_getchar(stream) & 0x3F);
return c;
case 3:
c = (c & 0x0F) << 12;
c += (((uint32_t) text_stream_getchar(stream) & 0x3F) << 6);
c += ((uint32_t) text_stream_getchar(stream) & 0x3F);
return c;
case 4:
c = (c & 0x7) << 19;
c += (((uint32_t) text_stream_getchar(stream) & 0x3F) << 12);
c += (((uint32_t) text_stream_getchar(stream) & 0x3F) << 6);
c += ((uint32_t) text_stream_getchar(stream) & 0x3F);
return c;
default:
return 0;
}
}
static void ms_rtt_4103_sink_init(MSFilter *f) {
RealTimeTextSinkData *s = ms_new0(RealTimeTextSinkData, 1);
s->pt_red = 0;
s->pt_t140 = 0;
f->data = s;
}
static void ms_rtt_4103_sink_preprocess(MSFilter *f) {
}
static void ms_rtt_4103_sink_process(MSFilter *f) {
RealTimeTextSinkData *s = (RealTimeTextSinkData *)f->data;
mblk_t *im;
ms_filter_lock(f);
while((im = ms_queue_get(f->inputs[0])) != NULL) {
read_text_packet(s, im);
if (text_stream_ischar(s)) {
uint32_t character = text_stream_getchar32(s);
RealtimeTextReceivedCharacter *data = ms_new0(RealtimeTextReceivedCharacter, 1);
data->character = character;
if (character != 0) {
ms_debug("Received char 32: %lu", (long unsigned) character);
ms_filter_notify(f, MS_RTT_4103_RECEIVED_CHAR, data);
}
}
}
ms_filter_unlock(f);
}
static void ms_rtt_4103_sink_postprocess(MSFilter *f) {
}
static void ms_rtt_4103_sink_uninit(MSFilter *f) {
ms_free(f->data);
}
static int ms_rtt_4103_sink_set_t140_payload(MSFilter *f, void *t140) {
RealTimeTextSinkData *s = (RealTimeTextSinkData *)f->data;
ms_filter_lock(f);
s->pt_t140 = *(int *)t140;
ms_debug("T140 payload number is %i", s->pt_t140);
ms_filter_unlock(f);
return 0;
}
static int ms_rtt_4103_sink_set_red_payload(MSFilter *f, void *red) {
RealTimeTextSinkData *s = (RealTimeTextSinkData *)f->data;
ms_filter_lock(f);
s->pt_red = *(int *)red;
ms_debug("RED payload number is %i", s->pt_red);
ms_filter_unlock(f);
return 0;
}
static MSFilterMethod ms_rtt_4103_sink_methods[] = {
{ MS_RTT_4103_SINK_SET_T140_PAYLOAD_TYPE_NUMBER, ms_rtt_4103_sink_set_t140_payload },
{ MS_RTT_4103_SINK_SET_RED_PAYLOAD_TYPE_NUMBER, ms_rtt_4103_sink_set_red_payload },
{ 0, NULL }
};
MSFilterDesc ms_rtt_4103_sink_desc = {
MS_RTT_4103_SINK_ID,
"MSRTT4103Sink",
"A filter to receive real time text",
MS_FILTER_OTHER,
NULL,
1,
0,
ms_rtt_4103_sink_init,
ms_rtt_4103_sink_preprocess,
ms_rtt_4103_sink_process,
ms_rtt_4103_sink_postprocess,
ms_rtt_4103_sink_uninit,
ms_rtt_4103_sink_methods
};
MS_FILTER_DESC_EXPORT(ms_rtt_4103_sink_desc)
\ No newline at end of file
/*
* rfc4103_source.cpp - Real time text RFC 4103 sender.
*
* Copyright (C) 2015 Belledonne Communications, Grenoble, France
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/msrtt4103.h"
typedef struct _RealTimeTextSourceData {
uint8_t buf[TS_NUMBER_OF_OUTBUF][TS_OUTBUF_SIZE];
size_t bufsize[TS_NUMBER_OF_OUTBUF];
int pribuf;
uint32_t timestamp[TS_NUMBER_OF_OUTBUF];
int pt_t140;
int pt_red;
} RealTimeTextSourceData;
static uint32_t get_prev_time(const RealTimeTextSourceData *stream) {
return stream->timestamp[stream->pribuf ? stream->pribuf-1 : TS_REDGEN];
}
static int get_last_buf(const RealTimeTextSourceData *stream) {
return stream->pribuf == TS_REDGEN ? 0 : stream->pribuf + 1;
}
static void use_next_buf(RealTimeTextSourceData *stream) {
stream->pribuf = get_last_buf(stream);
stream->bufsize[stream->pribuf] = 0;
stream->timestamp[stream->pribuf] = 0;
}
static int get_next_buf(const RealTimeTextSourceData *stream, const int cur) {
if (cur == stream->pribuf){
return -1;
}
return cur == TS_REDGEN ? 0 : cur + 1;
}
static uint32_t get_red_subheader(int pt, int offset, size_t length) {
return (1 << 31) | ((0x7F & pt) << 24) | ((0x3FFF & offset) << 10) | (0x3FF & (int)length);
}
static mblk_t *realtime_text_stream_generate_red_packet(RealTimeTextSourceData *stream) {
int cur = get_last_buf(stream);
uint8_t t140 = (stream->pt_t140 & 0x7F);
int pri = stream->pribuf;
mblk_t *packet;
uint8_t payload[TS_OUTBUF_SIZE * TS_NUMBER_OF_OUTBUF + 4 * TS_REDGEN + 1];
size_t payloadsize = 0;
int mark = 1;
/* this makes it possible to generate t140 with same function... */
if (stream->pt_red > 0) {
while (cur != pri) {
uint32_t diff = stream->timestamp[pri] - stream->timestamp[cur];
size_t size = stream->bufsize[cur];
uint32_t sub = htonl(get_red_subheader(t140, diff, size));
memcpy(&payload[payloadsize], &sub, 4);
payloadsize += 4;
cur = get_next_buf(stream, cur);
}
memcpy(&payload[payloadsize], &t140, 1);
payloadsize += 1;
cur = get_last_buf(stream);
while (cur != pri) {
if (stream->bufsize[cur]) {
mark = 0;
memcpy(&payload[payloadsize], &stream->buf[cur][0], stream->bufsize[cur]);
payloadsize += stream->bufsize[cur];
}
cur = get_next_buf(stream, cur);
}
}
if (stream->bufsize[pri]) {
memcpy(&payload[payloadsize], &stream->buf[pri][0], stream->bufsize[pri]);
payloadsize += stream->bufsize[pri];
}
packet = allocb(payloadsize, 0);
memcpy(packet->b_wptr, &payload[payloadsize], payloadsize);
packet->b_wptr += payloadsize;
mblk_set_marker_info(packet, mark);
return packet;
}
static void text_stream_putchar32(RealTimeTextSourceData *stream, uint32_t ic) {
int i = stream->pribuf;
uint8_t* c = &stream->buf[i][stream->bufsize[i]];
if (ic < 0x80) {
if (stream->bufsize[i] < TS_OUTBUF_SIZE) {
c[0] = ic;
stream->bufsize[i]++;
}
} else if (ic < 0x800) {
if (stream->bufsize[i] + 1 < TS_OUTBUF_SIZE) {
c[1] = 0x80 + ((ic & 0x3F));
c[0] = 0xC0 + ((ic >> 6) & 0x1F);
stream->bufsize[i] += 2;
}
} else if (ic < 0x100000) {
if (stream->bufsize[i] + 2 < TS_OUTBUF_SIZE) {
c[2] = 0x80 + (ic & 0x3F);
c[1] = 0x80 + ((ic >> 6) & 0x3F);
c[0] = 0xE0 + ((ic >> 12) & 0xF);
stream->bufsize[i] += 3;
}
} else if (ic < 0x110000) {
if (stream->bufsize[i] + 3 < TS_OUTBUF_SIZE) {
c[3] = 0x80 + (ic & 0x3F);
c[2] = 0x80 + ((ic >> 6) & 0x3F);
c[1] = 0x80 + ((ic >> 12) & 0x3F);
c[0] = 0xF0 + ((ic >> 18) & 0x7);
stream->bufsize[i] += 4;
}
}
}
static bool_t is_data_to_send(const RealTimeTextSourceData *stream) {
int i;
for (i = 0; i <= TS_REDGEN; i++) {
if (stream->bufsize[i]) {
return TRUE;
}
}
return FALSE;
}
static mblk_t* send_data(RealTimeTextSourceData *stream, uint32_t timestamp) {
int i = stream->pribuf;
uint32_t prevtime = get_prev_time(stream);
mblk_t *m = NULL;
if (is_data_to_send(stream)) {
if (timestamp < prevtime || (timestamp - prevtime) > TS_SEND_INTERVAL) {
stream->timestamp[i] = timestamp;
m = realtime_text_stream_generate_red_packet(stream);
mblk_set_timestamp_info(m, timestamp);
use_next_buf(stream);
}
} else {
if (timestamp < prevtime || (timestamp - prevtime) > TS_KEEP_ALIVE_INTERVAL) {
text_stream_putchar32(stream, 0xFEFF); /* BOM */
ms_debug("Sending BOM");
return send_data(stream, timestamp);
}
}
return m;
}
static void ms_rtt_4103_source_init(MSFilter *f) {
RealTimeTextSourceData *s = ms_new0(RealTimeTextSourceData, 1);
s->pt_red = 0;
s->pt_t140 = 0;
f->data = s;
}
static void ms_rtt_4103_source_preprocess(MSFilter *f) {
}
static void ms_rtt_4103_source_process(MSFilter *f) {
RealTimeTextSourceData *s = (RealTimeTextSourceData *)f->data;
uint64_t timems = f->ticker->time;
uint32_t timestamp = (uint32_t)(timems * 90);
ms_filter_lock(f);
mblk_t *m = send_data(s, timestamp);
if (m) {
ms_queue_put(f->outputs[0], m);
}
ms_filter_unlock(f);
}
static void ms_rtt_4103_source_postprocess(MSFilter *f) {
}
static void ms_rtt_4103_source_uninit(MSFilter *f) {
ms_free(f->data);
}
static int ms_rtt_4103_source_set_t140_payload(MSFilter *f, void *t140) {
RealTimeTextSourceData *s = (RealTimeTextSourceData *)f->data;
ms_filter_lock(f);
s->pt_t140 = *(int *)t140;
ms_debug("T140 payload number is %i", s->pt_t140);
ms_filter_unlock(f);
return 0;
}
static int ms_rtt_4103_source_set_red_payload(MSFilter *f, void *red) {
RealTimeTextSourceData *s = (RealTimeTextSourceData *)f->data;
ms_filter_lock(f);
s->pt_red = *(int *)