Libav 0.7.1
|
00001 /* 00002 * MxPEG clip file demuxer 00003 * Copyright (c) 2010 Anatoly Nenashev 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "libavutil/intreadwrite.h" 00023 #include "libavcodec/mjpeg.h" 00024 #include "avformat.h" 00025 #include "avio.h" 00026 00027 #define VIDEO_STREAM_INDEX 0 00028 #define AUDIO_STREAM_INDEX 1 00029 #define DEFAULT_PACKET_SIZE 1024 00030 #define OVERREAD_SIZE 3 00031 00032 typedef struct MXGContext { 00033 uint8_t *buffer; 00034 uint8_t *buffer_ptr; 00035 uint8_t *soi_ptr; 00036 unsigned int buffer_size; 00037 int64_t dts; 00038 unsigned int cache_size; 00039 } MXGContext; 00040 00041 static int mxg_read_header(AVFormatContext *s, AVFormatParameters *ap) 00042 { 00043 AVStream *video_st, *audio_st; 00044 MXGContext *mxg = s->priv_data; 00045 00046 /* video parameters will be extracted from the compressed bitstream */ 00047 video_st = av_new_stream(s, VIDEO_STREAM_INDEX); 00048 if (!video_st) 00049 return AVERROR(ENOMEM); 00050 video_st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 00051 video_st->codec->codec_id = CODEC_ID_MXPEG; 00052 av_set_pts_info(video_st, 64, 1, 1000000); 00053 00054 audio_st = av_new_stream(s, AUDIO_STREAM_INDEX); 00055 if (!audio_st) 00056 return AVERROR(ENOMEM); 00057 audio_st->codec->codec_type = AVMEDIA_TYPE_AUDIO; 00058 audio_st->codec->codec_id = CODEC_ID_PCM_ALAW; 00059 audio_st->codec->channels = 1; 00060 audio_st->codec->sample_rate = 8000; 00061 audio_st->codec->bits_per_coded_sample = 8; 00062 audio_st->codec->block_align = 1; 00063 av_set_pts_info(audio_st, 64, 1, 1000000); 00064 00065 mxg->soi_ptr = mxg->buffer_ptr = mxg->buffer = 0; 00066 mxg->buffer_size = 0; 00067 mxg->dts = AV_NOPTS_VALUE; 00068 mxg->cache_size = 0; 00069 00070 return 0; 00071 } 00072 00073 static uint8_t* mxg_find_startmarker(uint8_t *p, uint8_t *end) 00074 { 00075 for (; p < end - 3; p += 4) { 00076 uint32_t x = *(uint32_t*)p; 00077 00078 if (x & (~(x+0x01010101)) & 0x80808080) { 00079 if (p[0] == 0xff) { 00080 return p; 00081 } else if (p[1] == 0xff) { 00082 return p+1; 00083 } else if (p[2] == 0xff) { 00084 return p+2; 00085 } else if (p[3] == 0xff) { 00086 return p+3; 00087 } 00088 } 00089 } 00090 00091 for (; p < end; ++p) { 00092 if (*p == 0xff) return p; 00093 } 00094 00095 return end; 00096 } 00097 00098 static int mxg_update_cache(AVFormatContext *s, unsigned int cache_size) 00099 { 00100 MXGContext *mxg = s->priv_data; 00101 unsigned int current_pos = mxg->buffer_ptr - mxg->buffer; 00102 unsigned int soi_pos; 00103 int ret; 00104 00105 /* reallocate internal buffer */ 00106 if (current_pos > current_pos + cache_size) 00107 return AVERROR(ENOMEM); 00108 if (mxg->soi_ptr) soi_pos = mxg->soi_ptr - mxg->buffer; 00109 mxg->buffer = av_fast_realloc(mxg->buffer, &mxg->buffer_size, 00110 current_pos + cache_size + 00111 FF_INPUT_BUFFER_PADDING_SIZE); 00112 if (!mxg->buffer) 00113 return AVERROR(ENOMEM); 00114 mxg->buffer_ptr = mxg->buffer + current_pos; 00115 if (mxg->soi_ptr) mxg->soi_ptr = mxg->buffer + soi_pos; 00116 00117 /* get data */ 00118 ret = avio_read(s->pb, mxg->buffer_ptr + mxg->cache_size, 00119 cache_size - mxg->cache_size); 00120 if (ret < 0) 00121 return ret; 00122 00123 mxg->cache_size += ret; 00124 00125 return ret; 00126 } 00127 00128 static int mxg_read_packet(AVFormatContext *s, AVPacket *pkt) 00129 { 00130 int ret; 00131 unsigned int size; 00132 uint8_t *startmarker_ptr, *end, *search_end, marker; 00133 MXGContext *mxg = s->priv_data; 00134 00135 while (!s->pb->eof_reached && !s->pb->error){ 00136 if (mxg->cache_size <= OVERREAD_SIZE) { 00137 /* update internal buffer */ 00138 ret = mxg_update_cache(s, DEFAULT_PACKET_SIZE + OVERREAD_SIZE); 00139 if (ret < 0) 00140 return ret; 00141 } 00142 end = mxg->buffer_ptr + mxg->cache_size; 00143 00144 /* find start marker - 0xff */ 00145 if (mxg->cache_size > OVERREAD_SIZE) { 00146 search_end = end - OVERREAD_SIZE; 00147 startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end); 00148 } else { 00149 search_end = end; 00150 startmarker_ptr = mxg_find_startmarker(mxg->buffer_ptr, search_end); 00151 if (startmarker_ptr >= search_end - 1 || 00152 *(startmarker_ptr + 1) != EOI) break; 00153 } 00154 00155 if (startmarker_ptr != search_end) { /* start marker found */ 00156 marker = *(startmarker_ptr + 1); 00157 mxg->buffer_ptr = startmarker_ptr + 2; 00158 mxg->cache_size = end - mxg->buffer_ptr; 00159 00160 if (marker == SOI) { 00161 mxg->soi_ptr = startmarker_ptr; 00162 } else if (marker == EOI) { 00163 if (!mxg->soi_ptr) { 00164 av_log(s, AV_LOG_WARNING, "Found EOI before SOI, skipping\n"); 00165 continue; 00166 } 00167 00168 pkt->pts = pkt->dts = mxg->dts; 00169 pkt->stream_index = VIDEO_STREAM_INDEX; 00170 pkt->destruct = NULL; 00171 pkt->size = mxg->buffer_ptr - mxg->soi_ptr; 00172 pkt->data = mxg->soi_ptr; 00173 00174 if (mxg->soi_ptr - mxg->buffer > mxg->cache_size) { 00175 if (mxg->cache_size > 0) { 00176 memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size); 00177 } 00178 00179 mxg->buffer_ptr = mxg->buffer; 00180 } 00181 mxg->soi_ptr = 0; 00182 00183 return pkt->size; 00184 } else if ( (SOF0 <= marker && marker <= SOF15) || 00185 (SOS <= marker && marker <= COM) ) { 00186 /* all other markers that start marker segment also contain 00187 length value (see specification for JPEG Annex B.1) */ 00188 size = AV_RB16(mxg->buffer_ptr); 00189 if (size < 2) 00190 return AVERROR(EINVAL); 00191 00192 if (mxg->cache_size < size) { 00193 ret = mxg_update_cache(s, size); 00194 if (ret < 0) 00195 return ret; 00196 startmarker_ptr = mxg->buffer_ptr - 2; 00197 mxg->cache_size = 0; 00198 } else { 00199 mxg->cache_size -= size; 00200 } 00201 00202 mxg->buffer_ptr += size; 00203 00204 if (marker == APP13 && size >= 16) { /* audio data */ 00205 /* time (GMT) of first sample in usec since 1970, little-endian */ 00206 pkt->pts = pkt->dts = AV_RL64(startmarker_ptr + 8); 00207 pkt->stream_index = AUDIO_STREAM_INDEX; 00208 pkt->destruct = NULL; 00209 pkt->size = size - 14; 00210 pkt->data = startmarker_ptr + 16; 00211 00212 if (startmarker_ptr - mxg->buffer > mxg->cache_size) { 00213 if (mxg->cache_size > 0) { 00214 memcpy(mxg->buffer, mxg->buffer_ptr, mxg->cache_size); 00215 } 00216 mxg->buffer_ptr = mxg->buffer; 00217 } 00218 00219 return pkt->size; 00220 } else if (marker == COM && size >= 18 && 00221 !strncmp(startmarker_ptr + 4, "MXF", 3)) { 00222 /* time (GMT) of video frame in usec since 1970, little-endian */ 00223 mxg->dts = AV_RL64(startmarker_ptr + 12); 00224 } 00225 } 00226 } else { 00227 /* start marker not found */ 00228 mxg->buffer_ptr = search_end; 00229 mxg->cache_size = OVERREAD_SIZE; 00230 } 00231 } 00232 00233 return AVERROR_EOF; 00234 } 00235 00236 static int mxg_close(struct AVFormatContext *s) 00237 { 00238 MXGContext *mxg = s->priv_data; 00239 av_freep(&mxg->buffer); 00240 return 0; 00241 } 00242 00243 AVInputFormat ff_mxg_demuxer = { 00244 .name = "mxg", 00245 .long_name = NULL_IF_CONFIG_SMALL("MxPEG clip file format"), 00246 .priv_data_size = sizeof(MXGContext), 00247 .read_header = mxg_read_header, 00248 .read_packet = mxg_read_packet, 00249 .read_close = mxg_close, 00250 .extensions = "mxg" 00251 };