619 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			619 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Various utilities for ffmpeg system
 | |
|  * Copyright (c) 2000,2001 Gerard Lantau
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program 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 General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
|  */
 | |
| #include "avformat.h"
 | |
| #ifndef CONFIG_WIN32
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/time.h>
 | |
| #include <time.h>
 | |
| #else
 | |
| #define strcasecmp _stricmp
 | |
| #include <sys/types.h>
 | |
| #include <sys/timeb.h>
 | |
| #endif
 | |
| 
 | |
| AVFormat *first_format;
 | |
| 
 | |
| void register_avformat(AVFormat *format)
 | |
| {
 | |
|     AVFormat **p;
 | |
|     p = &first_format;
 | |
|     while (*p != NULL) p = &(*p)->next;
 | |
|     *p = format;
 | |
|     format->next = NULL;
 | |
| }
 | |
| 
 | |
| int match_ext(const char *filename, const char *extensions)
 | |
| {
 | |
|     const char *ext, *p;
 | |
|     char ext1[32], *q;
 | |
| 
 | |
|     ext = strrchr(filename, '.');
 | |
|     if (ext) {
 | |
|         ext++;
 | |
|         p = extensions;
 | |
|         for(;;) {
 | |
|             q = ext1;
 | |
|             while (*p != '\0' && *p != ',') 
 | |
|                 *q++ = *p++;
 | |
|             *q = '\0';
 | |
|             if (!strcasecmp(ext1, ext)) 
 | |
|                 return 1;
 | |
|             if (*p == '\0') 
 | |
|                 break;
 | |
|             p++;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| AVFormat *guess_format(const char *short_name, const char *filename, const char *mime_type)
 | |
| {
 | |
|     AVFormat *fmt, *fmt_found;
 | |
|     int score_max, score;
 | |
| 
 | |
|     /* find the proper file type */
 | |
|     fmt_found = NULL;
 | |
|     score_max = 0;
 | |
|     fmt = first_format;
 | |
|     while (fmt != NULL) {
 | |
|         score = 0;
 | |
|         if (fmt->name && short_name && !strcmp(fmt->name, short_name))
 | |
|             score += 100;
 | |
|         if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
 | |
|             score += 10;
 | |
|         if (filename && fmt->extensions && 
 | |
|             match_ext(filename, fmt->extensions)) {
 | |
|             score += 5;
 | |
|         }
 | |
|         if (score > score_max) {
 | |
|             score_max = score;
 | |
|             fmt_found = fmt;
 | |
|         }
 | |
|         fmt = fmt->next;
 | |
|     }
 | |
|     return fmt_found;
 | |
| }   
 | |
| 
 | |
| /* return TRUE if val is a prefix of str. If it returns TRUE, ptr is
 | |
|    set to the next character in 'str' after the prefix */
 | |
| int strstart(const char *str, const char *val, const char **ptr)
 | |
| {
 | |
|     const char *p, *q;
 | |
|     p = str;
 | |
|     q = val;
 | |
|     while (*q != '\0') {
 | |
|         if (*p != *q)
 | |
|             return 0;
 | |
|         p++;
 | |
|         q++;
 | |
|     }
 | |
|     if (ptr)
 | |
|         *ptr = p;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| void nstrcpy(char *buf, int buf_size, const char *str)
 | |
| {
 | |
|     int c;
 | |
|     char *q = buf;
 | |
| 
 | |
|     for(;;) {
 | |
|         c = *str++;
 | |
|         if (c == 0 || q >= buf + buf_size - 1)
 | |
|             break;
 | |
|         *q++ = c;
 | |
|     }
 | |
|     *q = '\0';
 | |
| }
 | |
| 
 | |
| void register_all(void)
 | |
| {
 | |
|     avcodec_init();
 | |
|     avcodec_register_all();
 | |
| 
 | |
|     register_avformat(&mp2_format);
 | |
|     register_avformat(&ac3_format);
 | |
|     register_avformat(&mpeg_mux_format);
 | |
|     register_avformat(&mpeg1video_format);
 | |
|     register_avformat(&mjpeg_format);
 | |
|     register_avformat(&h263_format);
 | |
|     register_avformat(&rm_format);
 | |
|     register_avformat(&asf_format);
 | |
|     register_avformat(&avi_format);
 | |
|     register_avformat(&mpjpeg_format);
 | |
|     register_avformat(&jpeg_format);
 | |
|     register_avformat(&single_jpeg_format);
 | |
|     register_avformat(&swf_format);
 | |
|     register_avformat(&wav_format);
 | |
|     register_avformat(&pcm_s16le_format);
 | |
|     register_avformat(&pcm_s16be_format);
 | |
|     register_avformat(&pcm_u16le_format);
 | |
|     register_avformat(&pcm_u16be_format);
 | |
|     register_avformat(&pcm_s8_format);
 | |
|     register_avformat(&pcm_u8_format);
 | |
|     register_avformat(&pcm_mulaw_format);
 | |
|     register_avformat(&pcm_alaw_format);
 | |
|     register_avformat(&rawvideo_format);
 | |
| #ifndef CONFIG_WIN32
 | |
|     register_avformat(&ffm_format);
 | |
| #endif
 | |
|     register_avformat(&pgm_format);
 | |
|     register_avformat(&ppm_format);
 | |
|     register_avformat(&pgmyuv_format);
 | |
|     register_avformat(&imgyuv_format);
 | |
|     register_avformat(&pgmpipe_format);
 | |
|     register_avformat(&pgmyuvpipe_format);
 | |
|     register_avformat(&ppmpipe_format);
 | |
| #ifdef CONFIG_GRAB
 | |
|     register_avformat(&video_grab_device_format);
 | |
|     register_avformat(&audio_device_format);
 | |
| #endif
 | |
| 
 | |
|     /* file protocols */
 | |
|     register_protocol(&file_protocol);
 | |
|     register_protocol(&pipe_protocol);
 | |
| #ifndef CONFIG_WIN32
 | |
|     register_protocol(&udp_protocol);
 | |
|     register_protocol(&http_protocol);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* memory handling */
 | |
| 
 | |
| int av_new_packet(AVPacket *pkt, int size)
 | |
| {
 | |
|     pkt->data = malloc(size);
 | |
|     if (!pkt->data)
 | |
|         return -ENOMEM;
 | |
|     pkt->size = size;
 | |
|     /* sane state */
 | |
|     pkt->pts = 0;
 | |
|     pkt->stream_index = 0;
 | |
|     pkt->flags = 0;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void av_free_packet(AVPacket *pkt)
 | |
| {
 | |
|     free(pkt->data);
 | |
|     /* fail safe */
 | |
|     pkt->data = NULL;
 | |
|     pkt->size = 0;
 | |
| }
 | |
| 
 | |
| /* fifo handling */
 | |
| 
 | |
| int fifo_init(FifoBuffer *f, int size)
 | |
| {
 | |
|     f->buffer = malloc(size);
 | |
|     if (!f->buffer)
 | |
|         return -1;
 | |
|     f->end = f->buffer + size;
 | |
|     f->wptr = f->rptr = f->buffer;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void fifo_free(FifoBuffer *f)
 | |
| {
 | |
|     free(f->buffer);
 | |
| }
 | |
| 
 | |
| int fifo_size(FifoBuffer *f, UINT8 *rptr)
 | |
| {
 | |
|     int size;
 | |
| 
 | |
|     if (f->wptr >= rptr) {
 | |
|         size = f->wptr - rptr;
 | |
|     } else {
 | |
|         size = (f->end - rptr) + (f->wptr - f->buffer);
 | |
|     }
 | |
|     return size;
 | |
| }
 | |
| 
 | |
| /* get data from the fifo (return -1 if not enough data) */
 | |
| int fifo_read(FifoBuffer *f, UINT8 *buf, int buf_size, UINT8 **rptr_ptr)
 | |
| {
 | |
|     UINT8 *rptr = *rptr_ptr;
 | |
|     int size, len;
 | |
| 
 | |
|     if (f->wptr >= rptr) {
 | |
|         size = f->wptr - rptr;
 | |
|     } else {
 | |
|         size = (f->end - rptr) + (f->wptr - f->buffer);
 | |
|     }
 | |
|     
 | |
|     if (size < buf_size)
 | |
|         return -1;
 | |
|     while (buf_size > 0) {
 | |
|         len = f->end - rptr;
 | |
|         if (len > buf_size)
 | |
|             len = buf_size;
 | |
|         memcpy(buf, rptr, len);
 | |
|         buf += len;
 | |
|         rptr += len;
 | |
|         if (rptr >= f->end)
 | |
|             rptr = f->buffer;
 | |
|         buf_size -= len;
 | |
|     }
 | |
|     *rptr_ptr = rptr;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void fifo_write(FifoBuffer *f, UINT8 *buf, int size, UINT8 **wptr_ptr)
 | |
| {
 | |
|     int len;
 | |
|     UINT8 *wptr;
 | |
|     wptr = *wptr_ptr;
 | |
|     while (size > 0) {
 | |
|         len = f->end - wptr;
 | |
|         if (len > size)
 | |
|             len = size;
 | |
|         memcpy(wptr, buf, len);
 | |
|         wptr += len;
 | |
|         if (wptr >= f->end)
 | |
|             wptr = f->buffer;
 | |
|         buf += len;
 | |
|         size -= len;
 | |
|     }
 | |
|     *wptr_ptr = wptr;
 | |
| }
 | |
| 
 | |
| /* media file handling. 
 | |
|    'filename' is the filename to open.
 | |
|    'format_name' is used to force the file format (NULL if auto guess).
 | |
|    'buf_size' is the optional buffer size (zero if default is OK).
 | |
|    'ap' are additionnal parameters needed when opening the file (NULL if default).
 | |
| */
 | |
| 
 | |
| AVFormatContext *av_open_input_file(const char *filename, 
 | |
|                                     const char *format_name,
 | |
|                                     int buf_size,
 | |
|                                     AVFormatParameters *ap)
 | |
| {
 | |
|     AVFormat *fmt;
 | |
|     AVFormatContext *ic = NULL;
 | |
|     int err;
 | |
| 
 | |
|     ic = av_mallocz(sizeof(AVFormatContext));
 | |
|     if (!ic)
 | |
|         goto fail;
 | |
| 
 | |
|     /* find format */
 | |
|     if (format_name != NULL) {
 | |
|         fmt = guess_format(format_name, NULL, NULL);
 | |
|     } else {
 | |
|         fmt = guess_format(NULL, filename, NULL);
 | |
|     }
 | |
|     if (!fmt || !fmt->read_header) {
 | |
|         return NULL;
 | |
|     }
 | |
|     ic->format = fmt;
 | |
| 
 | |
|     /* if no file needed do not try to open one */
 | |
|     if (!(fmt->flags & AVFMT_NOFILE)) {
 | |
|         if (url_fopen(&ic->pb, filename, URL_RDONLY) < 0)
 | |
|             goto fail;
 | |
|         if (buf_size > 0) {
 | |
|             url_setbufsize(&ic->pb, buf_size);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     err = ic->format->read_header(ic, ap);
 | |
|     if (err < 0) {
 | |
|         if (!(fmt->flags & AVFMT_NOFILE)) {
 | |
|             url_fclose(&ic->pb);
 | |
|         }
 | |
|         goto fail;
 | |
|     }
 | |
|     
 | |
|     return ic;
 | |
| 
 | |
|  fail:
 | |
|     if (ic)
 | |
|         free(ic);
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| int av_read_packet(AVFormatContext *s, AVPacket *pkt)
 | |
| {
 | |
|     AVPacketList *pktl;
 | |
| 
 | |
|     pktl = s->packet_buffer;
 | |
|     if (pktl) {
 | |
|         /* read packet from packet buffer, if there is data */
 | |
|         *pkt = pktl->pkt;
 | |
|         s->packet_buffer = pktl->next;
 | |
|         free(pktl);
 | |
|         return 0;
 | |
|     } else {
 | |
|         return s->format->read_packet(s, pkt);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void av_close_input_file(AVFormatContext *s)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     if (s->format->read_close)
 | |
|         s->format->read_close(s);
 | |
|     for(i=0;i<s->nb_streams;i++) {
 | |
|         free(s->streams[i]);
 | |
|     }
 | |
|     if (s->packet_buffer) {
 | |
|         AVPacketList *p, *p1;
 | |
|         p = s->packet_buffer;
 | |
|         while (p != NULL) {
 | |
|             p1 = p->next;
 | |
|             av_free_packet(&p->pkt);
 | |
|             free(p);
 | |
|             p = p1;
 | |
|         }
 | |
|         s->packet_buffer = NULL;
 | |
|     }
 | |
|     if (!(s->format->flags & AVFMT_NOFILE)) {
 | |
|         url_fclose(&s->pb);
 | |
|     }
 | |
|     free(s);
 | |
| }
 | |
| 
 | |
| 
 | |
| int av_write_packet(AVFormatContext *s, AVPacket *pkt)
 | |
| {
 | |
|     /* XXX: currently, an emulation because internal API must change */
 | |
|     return s->format->write_packet(s, pkt->stream_index, pkt->data, pkt->size);
 | |
| }
 | |
| 
 | |
| /* "user interface" functions */
 | |
| 
 | |
| void dump_format(AVFormatContext *ic,
 | |
|                  int index, 
 | |
|                  const char *url,
 | |
|                  int is_output)
 | |
| {
 | |
|     int i;
 | |
|     char buf[256];
 | |
| 
 | |
|     fprintf(stderr, "%s #%d, %s, %s '%s':\n", 
 | |
|             is_output ? "Output" : "Input",
 | |
|             index, ic->format->name, 
 | |
|             is_output ? "to" : "from", url);
 | |
|     for(i=0;i<ic->nb_streams;i++) {
 | |
|         AVStream *st = ic->streams[i];
 | |
|         avcodec_string(buf, sizeof(buf), &st->codec, is_output);
 | |
|         fprintf(stderr, "  Stream #%d.%d: %s\n", index, i, buf);
 | |
|     }
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
|     const char *str;
 | |
|     int width, height;
 | |
| } SizeEntry;
 | |
| 
 | |
| static SizeEntry sizes[] = {
 | |
|     { "sqcif", 128, 96 },
 | |
|     { "qcif", 176, 144 },
 | |
|     { "cif", 352, 288 },
 | |
|     { "4cif", 704, 576 },
 | |
| };
 | |
|     
 | |
| int parse_image_size(int *width_ptr, int *height_ptr, const char *str)
 | |
| {
 | |
|     int i;
 | |
|     int n = sizeof(sizes) / sizeof(SizeEntry);
 | |
|     const char *p;
 | |
|     int frame_width = 0, frame_height = 0;
 | |
| 
 | |
|     for(i=0;i<n;i++) {
 | |
|         if (!strcmp(sizes[i].str, str)) {
 | |
|             frame_width = sizes[i].width;
 | |
|             frame_height = sizes[i].height;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     if (i == n) {
 | |
|         p = str;
 | |
|         frame_width = strtol(p, (char **)&p, 10);
 | |
|         if (*p)
 | |
|             p++;
 | |
|         frame_height = strtol(p, (char **)&p, 10);
 | |
|     }
 | |
|     if (frame_width <= 0 || frame_height <= 0)
 | |
|         return -1;
 | |
|     *width_ptr = frame_width;
 | |
|     *height_ptr = frame_height;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| INT64 gettime(void)
 | |
| {
 | |
| #ifdef CONFIG_WIN32
 | |
|     struct _timeb tb;
 | |
|     _ftime(&tb);
 | |
|     return ((INT64)tb.time * INT64_C(1000) + (INT64)tb.millitm) * INT64_C(1000);
 | |
| #else
 | |
|     struct timeval tv;
 | |
|     gettimeofday(&tv,NULL);
 | |
|     return (INT64)tv.tv_sec * 1000000 + tv.tv_usec;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /* syntax: [YYYY-MM-DD ][[HH:]MM:]SS[.m...] . Return the date in micro seconds since 1970 */
 | |
| INT64 parse_date(const char *datestr, int duration)
 | |
| {
 | |
|     const char *p;
 | |
|     INT64 t;
 | |
|     int sec;
 | |
| 
 | |
|     p = datestr;
 | |
|     if (!duration) {
 | |
|         static const UINT8 months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 | |
|         int year, month, day, i;
 | |
| 
 | |
|         if (strlen(p) >= 5 && p[4] == '-') {
 | |
|             
 | |
|             year = strtol(p, (char **)&p, 10);
 | |
|             if (*p)
 | |
|                 p++;
 | |
|             month = strtol(p, (char **)&p, 10) - 1;
 | |
|             if (*p)
 | |
|                 p++;
 | |
|             day = strtol(p, (char **)&p, 10) - 1;
 | |
|             if (*p)
 | |
|                 p++;
 | |
|             day += (year - 1970) * 365;
 | |
|             /* if >= March, take February of current year into account too */
 | |
|             if (month >= 2)
 | |
|                 year++;
 | |
|             for(i=1970;i<year;i++) {
 | |
|                 if ((i % 100) == 0) {
 | |
|                     if ((i % 400) == 0) day++;
 | |
|                 } else if ((i % 4) == 0) {
 | |
|                     day++;
 | |
|                 }
 | |
|             }
 | |
|             for(i=0;i<month;i++)
 | |
|                 day += months[i];
 | |
|         } else {
 | |
|             day = (time(NULL) / (3600 * 24));
 | |
|         }
 | |
|         t = day * (3600 * 24);
 | |
|     } else {
 | |
|         t = 0;
 | |
|     }
 | |
|     
 | |
|     sec = 0;
 | |
|     for(;;) {
 | |
|         int val;
 | |
|         val = strtol(p, (char **)&p, 10);
 | |
|         sec = sec * 60 + val;
 | |
|         if (*p != ':')
 | |
|             break;
 | |
|         p++;
 | |
|     }
 | |
|     t = (t + sec) * 1000000;
 | |
|     if (*p == '.') {
 | |
|         int val, n;
 | |
|         p++;
 | |
|         n = strlen(p);
 | |
|         if (n > 6)
 | |
|             n = 6;
 | |
|         val = strtol(p, NULL, 10);
 | |
|         while (n < 6) {
 | |
|             val = val * 10;
 | |
|             n++;
 | |
|         }
 | |
|         t += val;
 | |
|     }
 | |
|     return t;
 | |
| }
 | |
| 
 | |
| /* syntax: '?tag1=val1&tag2=val2...'. No URL decoding is done. Return
 | |
|    1 if found */
 | |
| int find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
 | |
| {
 | |
|     const char *p;
 | |
|     char tag[128], *q;
 | |
| 
 | |
|     p = info;
 | |
|     if (*p == '?')
 | |
|         p++;
 | |
|     for(;;) {
 | |
|         q = tag;
 | |
|         while (*p != '\0' && *p != '=' && *p != '&') {
 | |
|             if ((q - tag) < sizeof(tag) - 1)
 | |
|                 *q++ = *p;
 | |
|             p++;
 | |
|         }
 | |
|         *q = '\0';
 | |
|         q = arg;
 | |
|         if (*p == '=') {
 | |
|             p++;
 | |
|             while (*p != '&' && *p != '\0') {
 | |
|                 if ((q - arg) < arg_size - 1)
 | |
|                     *q++ = *p;
 | |
|                 p++;
 | |
|             }
 | |
|             *q = '\0';
 | |
|         }
 | |
|         if (!strcmp(tag, tag1)) 
 | |
|             return 1;
 | |
|         if (*p != '&')
 | |
|             break;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Return in 'buf' the path with '%d' replaced by number. Also handles
 | |
|    the '%0nd' format where 'n' is the total number of digits and
 | |
|    '%%'. Return 0 if OK, and -1 if format error */
 | |
| int get_frame_filename(char *buf, int buf_size,
 | |
|                        const char *path, int number)
 | |
| {
 | |
|     const char *p;
 | |
|     char *q, buf1[20];
 | |
|     int nd, len, c, percentd_found;
 | |
| 
 | |
|     q = buf;
 | |
|     p = path;
 | |
|     percentd_found = 0;
 | |
|     for(;;) {
 | |
|         c = *p++;
 | |
|         if (c == '\0')
 | |
|             break;
 | |
|         if (c == '%') {
 | |
|             nd = 0;
 | |
|             while (*p >= '0' && *p <= '9') {
 | |
|                 nd = nd * 10 + *p++ - '0';
 | |
|             }
 | |
|             c = *p++;
 | |
|             switch(c) {
 | |
|             case '%':
 | |
|                 goto addchar;
 | |
|             case 'd':
 | |
|                 if (percentd_found)
 | |
|                     goto fail;
 | |
|                 percentd_found = 1;
 | |
|                 snprintf(buf1, sizeof(buf1), "%0*d", nd, number);
 | |
|                 len = strlen(buf1);
 | |
|                 if ((q - buf + len) > buf_size - 1)
 | |
|                     goto fail;
 | |
|                 memcpy(q, buf1, len);
 | |
|                 q += len;
 | |
|                 break;
 | |
|             default:
 | |
|                 goto fail;
 | |
|             }
 | |
|         } else {
 | |
|         addchar:
 | |
|             if ((q - buf) < buf_size - 1)
 | |
|                 *q++ = c;
 | |
|         }
 | |
|     }
 | |
|     if (!percentd_found)
 | |
|         goto fail;
 | |
|     *q = '\0';
 | |
|     return 0;
 | |
|  fail:
 | |
|     *q = '\0';
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| 
 |