Exemple #1
0
int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
                            uint8_t *buf, int buf_size)
{
    RTSPState *rt = s->priv_data;
    int id, len, i, ret;
    RTSPStream *rtsp_st;

#ifdef DEBUG_RTP_TCP
    av_dlog(s, "tcp_read_packet:\n");
#endif
redo:
    for (;;) {
        RTSPMessageHeader reply;

        ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL);
        if (ret < 0)
            return ret;
        if (ret == 1) /* received '$' */
            break;
        /* XXX: parse message */
        if (rt->state != RTSP_STATE_STREAMING)
            return 0;
    }
    ret = url_read_complete(rt->rtsp_hd, buf, 3);
    if (ret != 3)
        return -1;
    id  = buf[0];
    len = AV_RB16(buf + 1);
#ifdef DEBUG_RTP_TCP
    av_dlog(s, "id=%d len=%d\n", id, len);
#endif
    if (len > buf_size || len < 12)
        goto redo;
    /* get the data */
    ret = url_read_complete(rt->rtsp_hd, buf, len);
    if (ret != len)
        return -1;
    if (rt->transport == RTSP_TRANSPORT_RDT &&
        ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0)
        return -1;

    /* find the matching stream */
    for (i = 0; i < rt->nb_rtsp_streams; i++) {
        rtsp_st = rt->rtsp_streams[i];
        if (id >= rtsp_st->interleaved_min &&
            id <= rtsp_st->interleaved_max)
            goto found;
    }
    goto redo;
found:
    *prtsp_st = rtsp_st;
    return len;
}
Exemple #2
0
static int read_data_packet(MMSHContext *mmsh, const int len)
{
    MMSContext *mms   = &mmsh->mms;
    int res;
    if (len > sizeof(mms->in_buffer)) {
        av_log(NULL, AV_LOG_ERROR,
               "Data packet length %d exceeds the in_buffer size %zu\n",
               len, sizeof(mms->in_buffer));
        return AVERROR(EIO);
    }
    res = url_read_complete(mms->mms_hd, mms->in_buffer, len);
    av_dlog(NULL, "Data packet len = %d\n", len);
    if (res != len) {
        av_log(NULL, AV_LOG_ERROR, "Read data packet failed!\n");
        return AVERROR(EIO);
    }
    if (len > mms->asf_packet_len) {
        av_log(NULL, AV_LOG_ERROR,
               "Chunk length %d exceed packet length %d\n",len, mms->asf_packet_len);
        return AVERROR_INVALIDDATA;
    } else {
        memset(mms->in_buffer + len, 0, mms->asf_packet_len - len); // padding
    }
    mms->read_in_ptr      = mms->in_buffer;
    mms->remaining_in_len = mms->asf_packet_len;
    return 0;
}
Exemple #3
0
static ChunkType get_chunk_header(MMSHContext *mmsh, int *len)
{
    MMSContext *mms = &mmsh->mms;
    uint8_t chunk_header[CHUNK_HEADER_LENGTH];
    uint8_t ext_header[EXT_HEADER_LENGTH];
    ChunkType chunk_type;
    int chunk_len, res, ext_header_len;

    res = url_read_complete(mms->mms_hd, chunk_header, CHUNK_HEADER_LENGTH);
    if (res != CHUNK_HEADER_LENGTH) {
        av_log(NULL, AV_LOG_ERROR, "Read data packet header failed!\n");
        return AVERROR(EIO);
    }
    chunk_type = AV_RL16(chunk_header);
    chunk_len  = AV_RL16(chunk_header + 2);

    switch (chunk_type) {
    case CHUNK_TYPE_END:
    case CHUNK_TYPE_STREAM_CHANGE:
        ext_header_len = 4;
        break;
    case CHUNK_TYPE_ASF_HEADER:
    case CHUNK_TYPE_DATA:
        ext_header_len = 8;
        break;
    default:
        av_log(NULL, AV_LOG_ERROR, "Strange chunk type %d\n", chunk_type);
        return AVERROR_INVALIDDATA;
    }

    res = url_read_complete(mms->mms_hd, ext_header, ext_header_len);
    if (res != ext_header_len) {
        av_log(NULL, AV_LOG_ERROR, "Read ext header failed!\n");
        return AVERROR(EIO);
    }
    *len = chunk_len - ext_header_len;
    if (chunk_type == CHUNK_TYPE_END || chunk_type == CHUNK_TYPE_DATA)
        mmsh->chunk_seq = AV_RL32(ext_header);
    return chunk_type;
}
Exemple #4
0
static int get_http_header_data(MMSHContext *mmsh)
{
    MMSContext *mms = &mmsh->mms;
    int res, len;
    ChunkType chunk_type;

    for (;;) {
        len = 0;
        res = chunk_type = get_chunk_header(mmsh, &len);
        if (res < 0) {
            return res;
        } else if (chunk_type == CHUNK_TYPE_ASF_HEADER){
            // get asf header and stored it
            if (!mms->header_parsed) {
                if (mms->asf_header) {
                    if (len != mms->asf_header_size) {
                        mms->asf_header_size = len;
                        av_dlog(NULL, "Header len changed from %d to %d\n",
                                mms->asf_header_size, len);
                        av_freep(&mms->asf_header);
                    }
                }
                mms->asf_header = av_mallocz(len);
                if (!mms->asf_header) {
                    return AVERROR(ENOMEM);
                }
                mms->asf_header_size = len;
            }
            if (len > mms->asf_header_size) {
                av_log(NULL, AV_LOG_ERROR,
                       "Asf header packet len = %d exceed the asf header buf size %d\n",
                       len, mms->asf_header_size);
                return AVERROR(EIO);
            }
            res = url_read_complete(mms->mms_hd, mms->asf_header, len);
            if (res != len) {
                av_log(NULL, AV_LOG_ERROR,
                       "Recv asf header data len %d != expected len %d\n", res, len);
                return AVERROR(EIO);
            }
            mms->asf_header_size = len;
            if (!mms->header_parsed) {
                res = ff_mms_asf_header_parser(mms);
                mms->header_parsed = 1;
                return res;
            }
        } else if (chunk_type == CHUNK_TYPE_DATA) {
            // read data packet and do padding
            return read_data_packet(mmsh, len);
        } else {
            if (len) {
                if (len > sizeof(mms->in_buffer)) {
                    av_log(NULL, AV_LOG_ERROR,
                           "Other packet len = %d exceed the in_buffer size %zu\n",
                           len, sizeof(mms->in_buffer));
                    return AVERROR(EIO);
                }
                res = url_read_complete(mms->mms_hd, mms->in_buffer, len);
                if (res != len) {
                    av_log(NULL, AV_LOG_ERROR, "Read other chunk type data failed!\n");
                    return AVERROR(EIO);
                } else {
                    av_dlog(NULL, "Skip chunk type %d \n", chunk_type);
                    continue;
                }
            }
        }
    }
    return 0;
}
int StreamingRingBuffer::safe_read(void *data, uint sz)
{
    if (m_context)
        return url_read_complete(m_context, (unsigned char*)data, sz);
    return 0;
}
Exemple #6
0
int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p,
                        int chunk_size, RTMPPacket *prev_pkt)
{
    uint8_t hdr, t, buf[16];
    int channel_id, timestamp, data_size, offset = 0;
    uint32_t extra = 0;
    enum RTMPPacketType type;
    int size = 0;

    if (url_read(h, &hdr, 1) != 1)
        return AVERROR(EIO);
    size++;
    channel_id = hdr & 0x3F;

    if (channel_id < 2) { //special case for channel number >= 64
        buf[1] = 0;
        if (url_read_complete(h, buf, channel_id + 1) != channel_id + 1)
            return AVERROR(EIO);
        size += channel_id + 1;
        channel_id = AV_RL16(buf) + 64;
    }
    data_size = prev_pkt[channel_id].data_size;
    type      = prev_pkt[channel_id].type;
    extra     = prev_pkt[channel_id].extra;

    hdr >>= 6;
    if (hdr == RTMP_PS_ONEBYTE) {
        timestamp = prev_pkt[channel_id].ts_delta;
    } else {
        if (url_read_complete(h, buf, 3) != 3)
            return AVERROR(EIO);
        size += 3;
        timestamp = AV_RB24(buf);
        if (hdr != RTMP_PS_FOURBYTES) {
            if (url_read_complete(h, buf, 3) != 3)
                return AVERROR(EIO);
            size += 3;
            data_size = AV_RB24(buf);
            if (url_read_complete(h, buf, 1) != 1)
                return AVERROR(EIO);
            size++;
            type = buf[0];
            if (hdr == RTMP_PS_TWELVEBYTES) {
                if (url_read_complete(h, buf, 4) != 4)
                    return AVERROR(EIO);
                size += 4;
                extra = AV_RL32(buf);
            }
        }
        if (timestamp == 0xFFFFFF) {
            if (url_read_complete(h, buf, 4) != 4)
                return AVERROR(EIO);
            timestamp = AV_RB32(buf);
        }
    }
    if (hdr != RTMP_PS_TWELVEBYTES)
        timestamp += prev_pkt[channel_id].timestamp;

    if (ff_rtmp_packet_create(p, channel_id, type, timestamp, data_size))
        return -1;
    p->extra = extra;
    // save history
    prev_pkt[channel_id].channel_id = channel_id;
    prev_pkt[channel_id].type       = type;
    prev_pkt[channel_id].data_size  = data_size;
    prev_pkt[channel_id].ts_delta   = timestamp - prev_pkt[channel_id].timestamp;
    prev_pkt[channel_id].timestamp  = timestamp;
    prev_pkt[channel_id].extra      = extra;
    while (data_size > 0) {
        int toread = FFMIN(data_size, chunk_size);
        if (url_read_complete(h, p->data + offset, toread) != toread) {
            ff_rtmp_packet_destroy(p);
            return AVERROR(EIO);
        }
        data_size -= chunk_size;
        offset    += chunk_size;
        size      += chunk_size;
        if (data_size > 0) {
            url_read_complete(h, &t, 1); //marker
            size++;
            if (t != (0xC0 + channel_id))
                return -1;
        }
    }
    return size;
}
Exemple #7
0
int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p,
                        int chunk_size, RTMPPacket *prev_pkt)
{
    uint8_t hdr, t, buf[16];
    int channel_id, timestamp, data_size, offset = 0;
    uint32_t extra = 0;
    uint8_t type;

    if (url_read(h, &hdr, 1) != 1)
        return AVERROR(EIO);
    channel_id = hdr & 0x3F;

    data_size = prev_pkt[channel_id].data_size;
    type      = prev_pkt[channel_id].type;
    extra     = prev_pkt[channel_id].extra;

    hdr >>= 6;
    if (hdr == RTMP_PS_ONEBYTE) {
        //todo
        return -1;
    } else {
        if (url_read_complete(h, buf, 3) != 3)
            return AVERROR(EIO);
        timestamp = AV_RB24(buf);
        if (hdr != RTMP_PS_FOURBYTES) {
            if (url_read_complete(h, buf, 3) != 3)
                return AVERROR(EIO);
            data_size = AV_RB24(buf);
            if (url_read_complete(h, &type, 1) != 1)
                return AVERROR(EIO);
            if (hdr == RTMP_PS_TWELVEBYTES) {
                if (url_read_complete(h, buf, 4) != 4)
                    return AVERROR(EIO);
                extra = AV_RL32(buf);
            }
        }
    }
    if (ff_rtmp_packet_create(p, channel_id, type, timestamp, data_size))
        return -1;
    p->extra = extra;
    // save history
    prev_pkt[channel_id].channel_id = channel_id;
    prev_pkt[channel_id].type       = type;
    prev_pkt[channel_id].data_size  = data_size;
    prev_pkt[channel_id].timestamp  = timestamp;
    prev_pkt[channel_id].extra      = extra;
    while (data_size > 0) {
        int toread = FFMIN(data_size, chunk_size);
        if (url_read_complete(h, p->data + offset, toread) != toread) {
            ff_rtmp_packet_destroy(p);
            return AVERROR(EIO);
        }
        data_size -= chunk_size;
        offset    += chunk_size;
        if (data_size > 0) {
            url_read_complete(h, &t, 1); //marker
            if (t != (0xC0 + channel_id))
                return -1;
        }
    }
    return 0;
}
static int fill_buffer(stream_t *s, char *buffer, int max_len)
{
    int r = url_read_complete(s->priv, buffer, max_len);
    return (r <= 0) ? -1 : r;
}
Exemple #9
0
/** Read incoming MMST media, header or command packet. */
static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst)
{
    int read_result;
    MMSSCPacketType packet_type= -1;
    MMSContext *mms = &mmst->mms;
    for(;;) {
        read_result = url_read_complete(mms->mms_hd, mms->in_buffer, 8);
        if (read_result != 8) {
            if(read_result < 0) {
                av_log(NULL, AV_LOG_ERROR,
                       "Error reading packet header: %d (%s)\n",
                       read_result, strerror(read_result));
                packet_type = SC_PKT_CANCEL;
            } else {
                av_log(NULL, AV_LOG_ERROR,
                       "The server closed the connection\n");
                packet_type = SC_PKT_NO_DATA;
            }
            return packet_type;
        }

        // handle command packet.
        if(AV_RL32(mms->in_buffer + 4)==0xb00bface) {
            int length_remaining, hr;

            mmst->incoming_flags= mms->in_buffer[3];
            read_result= url_read_complete(mms->mms_hd, mms->in_buffer+8, 4);
            if(read_result != 4) {
                av_log(NULL, AV_LOG_ERROR,
                       "Reading command packet length failed: %d (%s)\n",
                       read_result,
                       read_result < 0 ? strerror(read_result) :
                           "The server closed the connection");
                return read_result < 0 ? read_result : AVERROR_IO;
            }

            length_remaining= AV_RL32(mms->in_buffer+8) + 4;
            dprintf(NULL, "Length remaining is %d\n", length_remaining);
            // read the rest of the packet.
            if (length_remaining < 0
                || length_remaining > sizeof(mms->in_buffer) - 12) {
                av_log(NULL, AV_LOG_ERROR,
                       "Incoming packet length %d exceeds bufsize %zu\n",
                       length_remaining, sizeof(mms->in_buffer) - 12);
                return AVERROR_INVALIDDATA;
            }
            read_result = url_read_complete(mms->mms_hd, mms->in_buffer + 12,
                                            length_remaining) ;
            if (read_result != length_remaining) {
                av_log(NULL, AV_LOG_ERROR,
                       "Reading pkt data (length=%d) failed: %d (%s)\n",
                       length_remaining, read_result,
                       read_result < 0 ? strerror(read_result) :
                           "The server closed the connection");
                return read_result < 0 ? read_result : AVERROR_IO;
            }
            packet_type= AV_RL16(mms->in_buffer+36);
            hr = AV_RL32(mms->in_buffer + 40);
            if (hr) {
                av_log(NULL, AV_LOG_ERROR,
                       "Server sent an error status code: 0x%08x\n", hr);
                return AVERROR_UNKNOWN;
            }
        } else {
            int length_remaining;
            int packet_id_type;
            int tmp;

            // note we cache the first 8 bytes,
            // then fill up the buffer with the others
            tmp                       = AV_RL16(mms->in_buffer + 6);
            length_remaining          = (tmp - 8) & 0xffff;
            mmst->incoming_packet_seq = AV_RL32(mms->in_buffer);
            packet_id_type            = mms->in_buffer[4];
            mmst->incoming_flags      = mms->in_buffer[5];

            if (length_remaining < 0
                || length_remaining > sizeof(mms->in_buffer) - 8) {
                av_log(NULL, AV_LOG_ERROR,
                       "Data length %d is invalid or too large (max=%zu)\n",
                       length_remaining, sizeof(mms->in_buffer));
                return AVERROR_INVALIDDATA;
            }
            mms->remaining_in_len    = length_remaining;
            mms->read_in_ptr         = mms->in_buffer;
            read_result= url_read_complete(mms->mms_hd, mms->in_buffer, length_remaining);
            if(read_result != length_remaining) {
                av_log(NULL, AV_LOG_ERROR,
                       "Failed to read packet data of size %d: %d (%s)\n",
                       length_remaining, read_result,
                       read_result < 0 ? strerror(read_result) :
                           "The server closed the connection");
                return read_result < 0 ? read_result : AVERROR_IO;
            }

            // if we successfully read everything.
            if(packet_id_type == mmst->header_packet_id) {
                packet_type = SC_PKT_ASF_HEADER;
                // Store the asf header
                if(!mms->header_parsed) {
                    void *p = av_realloc(mms->asf_header,
                                  mms->asf_header_size + mms->remaining_in_len);
                    if (!p) {
                        av_freep(&mms->asf_header);
                        return AVERROR(ENOMEM);
                    }
                    mms->asf_header = p;
                    memcpy(mms->asf_header + mms->asf_header_size,
                           mms->read_in_ptr, mms->remaining_in_len);
                    mms->asf_header_size += mms->remaining_in_len;
                }
                // 0x04 means asf header is sent in multiple packets.
                if (mmst->incoming_flags == 0x04)
                    continue;
            } else if(packet_id_type == mmst->packet_id) {
                packet_type = SC_PKT_ASF_MEDIA;
            } else {
                dprintf(NULL, "packet id type %d is old.", packet_id_type);
                continue;
            }
        }

        // preprocess some packet type
        if(packet_type == SC_PKT_KEEPALIVE) {
            send_keepalive_packet(mmst);
            continue;
        } else if(packet_type == SC_PKT_STREAM_CHANGING) {
            handle_packet_stream_changing_type(mmst);
        } else if(packet_type == SC_PKT_ASF_MEDIA) {
            pad_media_packet(mms);
        }
        return packet_type;
    }
}
Exemple #10
0
/** Read incoming MMST media, header or command packet. */
static MMSSCPacketType get_tcp_server_response(MMSContext *mms)
{
    int read_result;
    MMSSCPacketType packet_type= -1;

    for(;;) {
        if((read_result= url_read_complete(mms->mms_hd, mms->in_buffer, 8))==8) {
            // handle command packet.
            if(AV_RL32(mms->in_buffer + 4)==0xb00bface) {
                mms->incoming_flags= mms->in_buffer[3];
                read_result= url_read_complete(mms->mms_hd, mms->in_buffer+8, 4);
                if(read_result == 4) {
                    int length_remaining= AV_RL32(mms->in_buffer+8) + 4;
                    int hr;

                    dprintf(NULL, "Length remaining is %d\n", length_remaining);
                    // read the rest of the packet.
                    if (length_remaining < 0
                        || length_remaining > sizeof(mms->in_buffer) - 12) {
                        dprintf(NULL, "Incoming message len %d exceeds buffer len %d\n",
                            length_remaining, sizeof(mms->in_buffer) - 12);
                        return -1;
                    }
                    read_result = url_read_complete(mms->mms_hd, mms->in_buffer + 12,
                                                  length_remaining) ;
                    if (read_result == length_remaining) {
                        packet_type= AV_RL16(mms->in_buffer+36);
                    } else {
                        dprintf(NULL, "read for packet type failed%d!\n", read_result);
                        return -1;
                    }
                    hr = AV_RL32(mms->in_buffer + 40);
                    if (hr) {
                        dprintf(NULL, "The server side send back error code:0x%x\n", hr);
                        return -1;
                    }
                } else {
                    dprintf(NULL, "read for length remaining failed%d!\n", read_result);
                    return -1;
                }
            } else {
                int length_remaining;
                int packet_id_type;
                int tmp;

                // note we cache the first 8 bytes,
                // then fill up the buffer with the others
                tmp                       = AV_RL16(mms->in_buffer + 6);
                length_remaining          = (tmp - 8) & 0xffff;
                mms->incoming_packet_seq  = AV_RL32(mms->in_buffer);
                packet_id_type            = mms->in_buffer[4];
                mms->incoming_flags       = mms->in_buffer[5];

                if (length_remaining < 0
                        || length_remaining > sizeof(mms->in_buffer) - 8) {
                    dprintf(NULL, "Incoming data len %d exceeds buffer len %d\n",
                            length_remaining, sizeof(mms->in_buffer));
                    return -1;
                }
                mms->remaining_in_len    = length_remaining;
                mms->read_in_ptr         = mms->in_buffer;
                read_result= url_read_complete(mms->mms_hd, mms->in_buffer, length_remaining);
                if(read_result != length_remaining) {
                    dprintf(NULL, "read_bytes result: %d asking for %d\n",
                            read_result, length_remaining);
                    return -1;
                } else {
                    // if we successfully read everything.
                    if(packet_id_type == mms->header_packet_id) {
                        packet_type = SC_PKT_ASF_HEADER;
                        // Store the asf header
                        if(!mms->header_parsed) {
                            void *p = av_realloc(mms->asf_header,
                                              mms->asf_header_size
                                              + mms->remaining_in_len);
                            if (!p) {
                                av_freep(&mms->asf_header);
                                return AVERROR(ENOMEM);
                            }
                            mms->asf_header = p;
                            memcpy(mms->asf_header + mms->asf_header_size,
                                                 mms->read_in_ptr,
                                                 mms->remaining_in_len);
                            mms->asf_header_size += mms->remaining_in_len;
                        }
                        // 0x04 means asf header is sent in multiple packets.
                        if (mms->incoming_flags == 0x04)
                            continue;
                    } else if(packet_id_type == mms->packet_id) {
                        packet_type = SC_PKT_ASF_MEDIA;
                    } else {
                        dprintf(NULL, "packet id type %d is old.", packet_id_type);
                        continue;
                    }
                }
            }

            // preprocess some packet type
            if(packet_type == SC_PKT_KEEPALIVE) {
                send_keepalive_packet(mms);
                continue;
            } else if(packet_type == SC_PKT_STREAM_CHANGING) {
                handle_packet_stream_changing_type(mms);
            } else if(packet_type == SC_PKT_ASF_MEDIA) {
                pad_media_packet(mms);
            }
            return packet_type;
        } else {
            if(read_result<0) {
                dprintf(NULL, "Read error (or cancelled) returned %d!\n", read_result);
                packet_type = SC_PKT_CANCEL;
            } else {
                dprintf(NULL, "Read result of zero?!\n");
                packet_type = SC_PKT_NO_DATA;
            }
            return packet_type;
        }
    }
}
Exemple #11
0
int
ReadFunc2(void *opaque, uint8_t *buf, int size)
{
    DPRINTF(E_DBG, L_XCODE, "requested a read of %d bytes\n", size);
    struct icy_ctx *s = (struct icy_ctx *) opaque;
    URLContext *context = s->context;


    if ( !s->icy_metaint)
        return url_read_complete( context, buf, size );

    int len = url_read_complete( context, buf, size );
    int ldata = s->icy_metaint - s->data_pos;

    DPRINTF(E_DBG, L_XCODE, "current data pos %d\n", s->data_pos);
    /* pull out the Icy Metadata */
    if ( s->data_pos + len > s->icy_metaint ) {
        DPRINTF(E_DBG, L_XCODE, "current packet has metadata. ldata %d len %d data_pos %d\n", ldata, len, s->data_pos);
        while ( ldata < len ) {
            int md_size = *(buf + ldata) * 16;
            /* determine how much data to copy initially */
            DPRINTF(E_DBG, L_XCODE, "found a metatag of %d bytes\n", md_size);

            int md_len = (ldata + md_size + 1 > len) ? len - ldata - 1: md_size;

            if (md_size > 0) {
                /* parse out the metadata */
                uint8_t *md = (uint8_t*)av_malloc(sizeof(uint8_t) * (md_size + 1));
                if (md != NULL) {
                    int md_total = md_len;
                    /* copy in whatever metadata we already have */
                    memcpy(md, buf + ldata + 1, md_len);
                    /* get the rest of the metadata */
                    if (md_total < md_size) {
                        do {
                            md_total += url_read(context, md + md_total, md_size - md_total);
                        } while (md_total < md_size);
                    }
                }
                md[md_size] = 0;
                http_parse_metadata(s, md, md_size);
                av_free(md);
            }

            s->data_pos = 0;

            if (ldata + md_len + 1 < len) {
                /* copy any leftover data */
                int ridx = ldata + md_len + 1;
                int rlen = len - ridx;
                memcpy(buf + ldata, buf + ridx, rlen);
                s->data_pos = rlen;
            }
            ldata+= s->icy_metaint;

            /* zero length returns cause problems (metadata only bufs) */
            if ((len -= (md_len + 1)) == 0) {
                len = url_read(context, buf, size);
            }
        }
    } else if (s->icy_metaint) {
        s->data_pos += len;
    }
    DPRINTF(E_DBG, L_XCODE, "returning %d\n",len);
    return len;
}