yading@11: /* yading@11: * MxPEG clip file demuxer yading@11: * Copyright (c) 2010 Anatoly Nenashev 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 "libavutil/channel_layout.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavcodec/mjpeg.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: #include "avio.h" yading@11: yading@11: #define DEFAULT_PACKET_SIZE 1024 yading@11: #define OVERREAD_SIZE 3 yading@11: yading@11: typedef struct MXGContext { yading@11: uint8_t *buffer; yading@11: uint8_t *buffer_ptr; yading@11: uint8_t *soi_ptr; yading@11: unsigned int buffer_size; yading@11: int64_t dts; yading@11: unsigned int cache_size; yading@11: } MXGContext; yading@11: yading@11: static int mxg_read_header(AVFormatContext *s) yading@11: { yading@11: AVStream *video_st, *audio_st; yading@11: MXGContext *mxg = s->priv_data; yading@11: yading@11: /* video parameters will be extracted from the compressed bitstream */ yading@11: video_st = avformat_new_stream(s, NULL); yading@11: if (!video_st) yading@11: return AVERROR(ENOMEM); yading@11: video_st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: video_st->codec->codec_id = AV_CODEC_ID_MXPEG; yading@11: avpriv_set_pts_info(video_st, 64, 1, 1000000); yading@11: yading@11: audio_st = avformat_new_stream(s, NULL); yading@11: if (!audio_st) yading@11: return AVERROR(ENOMEM); yading@11: audio_st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: audio_st->codec->codec_id = AV_CODEC_ID_PCM_ALAW; yading@11: audio_st->codec->channels = 1; yading@11: audio_st->codec->channel_layout = AV_CH_LAYOUT_MONO; yading@11: audio_st->codec->sample_rate = 8000; yading@11: audio_st->codec->bits_per_coded_sample = 8; yading@11: audio_st->codec->block_align = 1; yading@11: avpriv_set_pts_info(audio_st, 64, 1, 1000000); yading@11: yading@11: mxg->soi_ptr = mxg->buffer_ptr = mxg->buffer = 0; yading@11: mxg->buffer_size = 0; yading@11: mxg->dts = AV_NOPTS_VALUE; yading@11: mxg->cache_size = 0; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static uint8_t* mxg_find_startmarker(uint8_t *p, uint8_t *end) yading@11: { yading@11: for (; p < end - 3; p += 4) { yading@11: uint32_t x = *(uint32_t*)p; yading@11: yading@11: if (x & (~(x+0x01010101)) & 0x80808080) { yading@11: if (p[0] == 0xff) { yading@11: return p; yading@11: } else if (p[1] == 0xff) { yading@11: return p+1; yading@11: } else if (p[2] == 0xff) { yading@11: return p+2; yading@11: } else if (p[3] == 0xff) { yading@11: return p+3; yading@11: } yading@11: } yading@11: } yading@11: yading@11: for (; p < end; ++p) { yading@11: if (*p == 0xff) return p; yading@11: } yading@11: yading@11: return end; yading@11: } yading@11: yading@11: static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size) yading@11: { yading@11: MXGContext *mxg = s->priv_data; yading@11: unsigned int current_pos = mxg->buffer_ptr - mxg->buffer; yading@11: unsigned int soi_pos; yading@11: uint8_t *buffer; yading@11: int ret; yading@11: yading@11: /* reallocate internal buffer */ yading@11: if (current_pos > current_pos + cache_size) yading@11: return AVERROR(ENOMEM); yading@11: soi_pos = mxg->soi_ptr - mxg->buffer; yading@11: buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size, yading@11: current_pos + cache_size + yading@11: FF_INPUT_BUFFER_PADDING_SIZE); yading@11: if (!buffer) yading@11: return AVERROR(ENOMEM); yading@11: mxg->buffer = buffer; yading@11: mxg->buffer_ptr = mxg->buffer + current_pos; yading@11: if (mxg->soi_ptr) mxg->soi_ptr = mxg->buffer + soi_pos; yading@11: yading@11: /* get data */ yading@11: ret = avio_read(s->pb, mxg->buffer_ptr + mxg->cache_size, yading@11: cache_size - mxg->cache_size); yading@11: if (ret < 0) yading@11: return ret; yading@11: yading@11: mxg->cache_size += ret; yading@11: yading@11: return ret; yading@11: } yading@11: yading@11: static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: int ret; yading@11: unsigned int size; yading@11: uint8_t *startmarker_ptr, *end, *search_end, marker; yading@11: MXGContext *mxg = s->priv_data; yading@11: yading@11: while (!url_feof(s->pb) && !s->pb->error){ yading@11: if (mxg->cache_size <= OVERREAD_SIZE) { yading@11: /* update internal buffer */ yading@11: ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE); yading@11: if (ret < 0) yading@11: return ret; yading@11: } yading@11: end = mxg->buffer_ptr + mxg->cache_size; yading@11: yading@11: /* find start marker - 0xff */ yading@11: if (mxg->cache_size > OVERREAD_SIZE) { yading@11: search_end = end - OVERREAD_SIZE; yading@11: startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end); yading@11: } else { yading@11: search_end = end; yading@11: startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end); yading@11: if (startmarker_ptr >= search_end - 1 || yading@11: *(startmarker_ptr + 1) != EOI) break; yading@11: } yading@11: yading@11: if (startmarker_ptr != search_end) { /* start marker found */ yading@11: marker = *(startmarker_ptr + 1); yading@11: mxg->buffer_ptr = startmarker_ptr + 2; yading@11: mxg->cache_size = end - mxg->buffer_ptr; yading@11: yading@11: if (marker == SOI) { yading@11: mxg->soi_ptr = startmarker_ptr; yading@11: } else if (marker == EOI) { yading@11: if (!mxg->soi_ptr) { yading@11: av_log(s, AV_LOG_WARNING, "Found EOI before SOI, skipping\n"); yading@11: continue; yading@11: } yading@11: yading@11: pkt->pts = pkt->dts = mxg->dts; yading@11: pkt->stream_index = 0; yading@11: #if FF_API_DESTRUCT_PACKET yading@11: pkt->destruct = NULL; yading@11: #endif yading@11: pkt->buf = NULL; yading@11: pkt->size = mxg->buffer_ptr - mxg->soi_ptr; yading@11: pkt->data = mxg->soi_ptr; yading@11: yading@11: if (mxg->soi_ptr - mxg->buffer > mxg->cache_size) { yading@11: if (mxg->cache_size > 0) { yading@11: memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size); yading@11: } yading@11: yading@11: mxg->buffer_ptr = mxg->buffer; yading@11: } yading@11: mxg->soi_ptr = 0; yading@11: yading@11: return pkt->size; yading@11: } else if ( (SOF0 <= marker && marker <= SOF15) || yading@11: (SOS <= marker && marker <= COM) ) { yading@11: /* all other markers that start marker segment also contain yading@11: length value (see specification for JPEG Annex B.1) */ yading@11: size = AV_RB16(mxg->buffer_ptr); yading@11: if (size < 2) yading@11: return AVERROR(EINVAL); yading@11: yading@11: if (mxg->cache_size < size) { yading@11: ret = mxg_update_cache(s, size); yading@11: if (ret < 0) yading@11: return ret; yading@11: startmarker_ptr = mxg->buffer_ptr - 2; yading@11: mxg->cache_size = 0; yading@11: } else { yading@11: mxg->cache_size -= size; yading@11: } yading@11: yading@11: mxg->buffer_ptr += size; yading@11: yading@11: if (marker == APP13 && size >= 16) { /* audio data */ yading@11: /* time (GMT) of first sample in usec since 1970, little-endian */ yading@11: pkt->pts = pkt->dts = AV_RL64(startmarker_ptr + 8); yading@11: pkt->stream_index = 1; yading@11: #if FF_API_DESTRUCT_PACKET yading@11: pkt->destruct = NULL; yading@11: #endif yading@11: pkt->buf = NULL; yading@11: pkt->size = size - 14; yading@11: pkt->data = startmarker_ptr + 16; yading@11: yading@11: if (startmarker_ptr - mxg->buffer > mxg->cache_size) { yading@11: if (mxg->cache_size > 0) { yading@11: memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size); yading@11: } yading@11: mxg->buffer_ptr = mxg->buffer; yading@11: } yading@11: yading@11: return pkt->size; yading@11: } else if (marker == COM && size >= 18 && yading@11: !strncmp(startmarker_ptr + 4, "MXF", 3)) { yading@11: /* time (GMT) of video frame in usec since 1970, little-endian */ yading@11: mxg->dts = AV_RL64(startmarker_ptr + 12); yading@11: } yading@11: } yading@11: } else { yading@11: /* start marker not found */ yading@11: mxg->buffer_ptr = search_end; yading@11: mxg->cache_size = OVERREAD_SIZE; yading@11: } yading@11: } yading@11: yading@11: return AVERROR_EOF; yading@11: } yading@11: yading@11: static int mxg_close(struct AVFormatContext *s) yading@11: { yading@11: MXGContext *mxg = s->priv_data; yading@11: av_freep(&mxg->buffer); yading@11: return 0; yading@11: } yading@11: yading@11: AVInputFormat ff_mxg_demuxer = { yading@11: .name = "mxg", yading@11: .long_name = NULL_IF_CONFIG_SMALL("MxPEG clip"), yading@11: .priv_data_size = sizeof(MXGContext), yading@11: .read_header = mxg_read_header, yading@11: .read_packet = mxg_read_packet, yading@11: .read_close = mxg_close, yading@11: .extensions = "mxg", yading@11: };