| /* | 
 |  * Copyright (C) 2008  Ramiro Polla | 
 |  * | 
 |  * This file is part of FFmpeg. | 
 |  * | 
 |  * FFmpeg is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * FFmpeg 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 | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with FFmpeg; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 
 |  */ | 
 |  | 
 | #include "libavcodec/bytestream.h" | 
 | #include "avformat.h" | 
 | #include "demux.h" | 
 | #include "internal.h" | 
 |  | 
 | #define HEADER_SIZE         24 | 
 |  | 
 | /* | 
 |  * Header structure: | 
 |  *  uint16_t    ss;     // struct size | 
 |  *  uint16_t    width;  // frame width | 
 |  *  uint16_t    height; // frame height | 
 |  *  uint16_t    ff;     // keyframe + some other info(???) | 
 |  *  uint32_t    size;   // size of data | 
 |  *  uint32_t    fourcc; // ML20 | 
 |  *  uint32_t    u3;     // ? | 
 |  *  uint32_t    ts;     // time | 
 |  */ | 
 |  | 
 | static int msnwc_tcp_probe(const AVProbeData *p) | 
 | { | 
 |     int i; | 
 |  | 
 |     for (i = 0; i + HEADER_SIZE <= p->buf_size; i++) { | 
 |         uint16_t width, height; | 
 |         uint32_t fourcc; | 
 |         const uint8_t *bytestream = p->buf + i; | 
 |  | 
 |         if (bytestream_get_le16(&bytestream) != HEADER_SIZE) | 
 |             continue; | 
 |         width  = bytestream_get_le16(&bytestream); | 
 |         height = bytestream_get_le16(&bytestream); | 
 |         if (!(width == 320 && | 
 |               height == 240) && !(width == 160 && height == 120)) | 
 |             continue; | 
 |         bytestream += 2; // keyframe | 
 |         bytestream += 4; // size | 
 |         fourcc      = bytestream_get_le32(&bytestream); | 
 |         if (fourcc != MKTAG('M', 'L', '2', '0')) | 
 |             continue; | 
 |  | 
 |         if (i) { | 
 |             if (i < 14) /* starts with SwitchBoard connection info */ | 
 |                 return AVPROBE_SCORE_MAX / 2; | 
 |             else        /* starts in the middle of stream */ | 
 |                 return AVPROBE_SCORE_MAX / 3; | 
 |         } else { | 
 |             return AVPROBE_SCORE_MAX; | 
 |         } | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | static int msnwc_tcp_read_header(AVFormatContext *ctx) | 
 | { | 
 |     AVIOContext *pb = ctx->pb; | 
 |     AVCodecParameters *par; | 
 |     AVStream *st; | 
 |  | 
 |     st = avformat_new_stream(ctx, NULL); | 
 |     if (!st) | 
 |         return AVERROR(ENOMEM); | 
 |  | 
 |     par             = st->codecpar; | 
 |     par->codec_type = AVMEDIA_TYPE_VIDEO; | 
 |     par->codec_id   = AV_CODEC_ID_MIMIC; | 
 |     par->codec_tag  = MKTAG('M', 'L', '2', '0'); | 
 |  | 
 |     avpriv_set_pts_info(st, 32, 1, 1000); | 
 |  | 
 |     /* Some files start with "connected\r\n\r\n". | 
 |      * So skip until we find the first byte of struct size */ | 
 |     while(avio_r8(pb) != HEADER_SIZE && !avio_feof(pb)) ; | 
 |  | 
 |     if(avio_feof(pb)) { | 
 |         av_log(ctx, AV_LOG_ERROR, "Could not find valid start.\n"); | 
 |         return AVERROR_INVALIDDATA; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | static int msnwc_tcp_read_packet(AVFormatContext *ctx, AVPacket *pkt) | 
 | { | 
 |     AVIOContext *pb = ctx->pb; | 
 |     uint16_t keyframe; | 
 |     uint32_t size, timestamp; | 
 |     int ret; | 
 |  | 
 |     avio_skip(pb, 1); /* one byte has been read ahead */ | 
 |     avio_skip(pb, 2); | 
 |     avio_skip(pb, 2); | 
 |     keyframe = avio_rl16(pb); | 
 |     size     = avio_rl32(pb); | 
 |     avio_skip(pb, 4); | 
 |     avio_skip(pb, 4); | 
 |     timestamp = avio_rl32(pb); | 
 |  | 
 |     if (!size) | 
 |         return AVERROR_INVALIDDATA; | 
 |  | 
 |     if ((ret = av_get_packet(pb, pkt, size)) < 0) | 
 |         return ret; | 
 |  | 
 |     avio_skip(pb, 1); /* Read ahead one byte of struct size like read_header */ | 
 |  | 
 |     pkt->pts          = timestamp; | 
 |     pkt->dts          = timestamp; | 
 |     pkt->stream_index = 0; | 
 |  | 
 |     /* Some aMsn generated videos (or was it Mercury Messenger?) don't set | 
 |      * this bit and rely on the codec to get keyframe information */ | 
 |     if (keyframe & 1) | 
 |         pkt->flags |= AV_PKT_FLAG_KEY; | 
 |  | 
 |     return HEADER_SIZE + size; | 
 | } | 
 |  | 
 | const FFInputFormat ff_msnwc_tcp_demuxer = { | 
 |     .p.name      = "msnwctcp", | 
 |     .p.long_name = NULL_IF_CONFIG_SMALL("MSN TCP Webcam stream"), | 
 |     .read_probe  = msnwc_tcp_probe, | 
 |     .read_header = msnwc_tcp_read_header, | 
 |     .read_packet = msnwc_tcp_read_packet, | 
 | }; |