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