]> git.sesse.net Git - ffmpeg/blobdiff - libav/utils.c
use codec_id so that the codec does not need to be opened
[ffmpeg] / libav / utils.c
index bae52c73d73e4437f76c3346bcdfc924e2a7a02d..5a9aa082f4ebaadca402403ee4297fb92b3e8e6e 100644 (file)
@@ -17,7 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include "avformat.h"
-#include "tick.h"
+#include <ctype.h>
 #ifndef CONFIG_WIN32
 #include <unistd.h>
 #include <fcntl.h>
 #endif
 #include <time.h>
 
+#ifndef HAVE_STRPTIME
+#include "strptime.h"
+#endif
+
 AVInputFormat *first_iformat;
 AVOutputFormat *first_oformat;
 
@@ -103,103 +107,33 @@ AVOutputFormat *guess_format(const char *short_name, const char *filename,
     return fmt_found;
 }   
 
-AVInputFormat *av_find_input_format(const char *short_name)
+AVOutputFormat *guess_stream_format(const char *short_name, const char *filename, 
+                             const char *mime_type)
 {
-    AVInputFormat *fmt;
-    for(fmt = first_iformat; fmt != NULL; fmt = fmt->next) {
-        if (!strcmp(fmt->name, short_name))
-            return fmt;
-    }
-    return NULL;
-}
+    AVOutputFormat *fmt = guess_format(short_name, filename, mime_type);
 
+    if (fmt) {
+        AVOutputFormat *stream_fmt;
+        char stream_format_name[64];
 
-/**
- * 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.
- *
- * @param str input string
- * @param val prefix to test
- * @param ptr updated after the prefix in str in there is a match
- * @return TRUE if there is a match
- */
-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;
-}
+        snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
+        stream_fmt = guess_format(stream_format_name, NULL, NULL);
 
-/**
- * Copy the string str to buf. If str length is bigger than buf_size -
- * 1 then it is clamped to buf_size - 1.
- * NOTE: this function does what strncpy should have done to be
- * useful. NEVER use strncpy.
- * 
- * @param buf destination buffer
- * @param buf_size size of destination buffer
- * @param str source string
- */
-void pstrcpy(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;
+        if (stream_fmt)
+            fmt = stream_fmt;
     }
-    *q = '\0';
+
+    return fmt;
 }
 
-void register_all(void)
+AVInputFormat *av_find_input_format(const char *short_name)
 {
-    avcodec_init();
-    avcodec_register_all();
-
-    mpegps_init();
-    mpegts_init();
-    crc_init();
-    img_init();
-    raw_init();
-    rm_init();
-    asf_init();
-    avienc_init();
-    avidec_init();
-    wav_init();
-    swf_init();
-    au_init();
-    gif_init();
-    mov_init();
-    jpeg_init();
-
-#ifndef CONFIG_WIN32
-    ffm_init();
-#endif
-#ifdef CONFIG_VIDEO4LINUX
-    video_grab_init();    
-#endif
-#ifdef CONFIG_AUDIO_OSS
-    audio_init();
-#endif
-    /* file protocols */
-    register_protocol(&file_protocol);
-    register_protocol(&pipe_protocol);
-#ifdef CONFIG_NETWORK
-    register_protocol(&udp_protocol);
-    register_protocol(&http_protocol);
-#endif
+    AVInputFormat *fmt;
+    for(fmt = first_iformat; fmt != NULL; fmt = fmt->next) {
+        if (!strcmp(fmt->name, short_name))
+            return fmt;
+    }
+    return NULL;
 }
 
 /* memory handling */
@@ -213,14 +147,19 @@ void register_all(void)
  */
 int av_new_packet(AVPacket *pkt, int size)
 {
-    pkt->data = av_malloc(size);
+    int i;
+    pkt->data = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
     if (!pkt->data)
         return AVERROR_NOMEM;
     pkt->size = size;
     /* sane state */
-    pkt->pts = 0;
+    pkt->pts = AV_NOPTS_VALUE;
     pkt->stream_index = 0;
     pkt->flags = 0;
+    
+    for(i=0; i<FF_INPUT_BUFFER_PADDING_SIZE; i++)
+        pkt->data[size+i]= 0;
+
     return 0;
 }
 
@@ -320,7 +259,7 @@ int filename_number_test(const char *filename)
 }
 
 /* guess file format */
-static AVInputFormat *probe_input_format(AVProbeData *pd, int is_opened)
+AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
 {
     AVInputFormat *fmt1, *fmt;
     int score, score_max;
@@ -384,7 +323,7 @@ int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
 
     if (!fmt) {
         /* guess format if no file can be opened  */
-        fmt = probe_input_format(pd, 0);
+        fmt = av_probe_input_format(pd, 0);
     }
 
     /* if no file needed do not try to open one */
@@ -396,14 +335,16 @@ int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
         if (buf_size > 0) {
             url_setbufsize(&ic->pb, buf_size);
         }
-        /* read probe data */
-        pd->buf_size = get_buffer(&ic->pb, buf, PROBE_BUF_SIZE);
-        url_fseek(&ic->pb, 0, SEEK_SET);
+        if (!fmt) {
+            /* read probe data */
+            pd->buf_size = get_buffer(&ic->pb, buf, PROBE_BUF_SIZE);
+            url_fseek(&ic->pb, 0, SEEK_SET);
+        }
     }
     
     /* guess file format */
     if (!fmt) {
-        fmt = probe_input_format(pd, 1);
+        fmt = av_probe_input_format(pd, 1);
     }
 
     /* if still no format found, error */
@@ -412,6 +353,14 @@ int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
         goto fail;
     }
         
+    /* XXX: suppress this hack for redirectors */
+    if (fmt == &redir_demux) {
+        err = redir_open(ic_ptr, &ic->pb);
+        url_fclose(&ic->pb);
+        av_free(ic);
+        return err;
+    }
+
     ic->iformat = fmt;
 
     /* allocate private data */
@@ -421,6 +370,9 @@ int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
         goto fail;
     }
 
+    /* default pts settings is MPEG like */
+    av_set_pts_info(ic, 33, 1, 90000);
+
     /* check filename in case of an image number is expected */
     if (ic->iformat->flags & AVFMT_NEEDNUMBER) {
         if (filename_number_test(ic->filename) < 0) { 
@@ -596,6 +548,9 @@ int av_find_stream_info(AVFormatContext *ic)
                 st->codec_info_state = CSTATE_FOUND; 
                 codec = avcodec_find_decoder(st->codec.codec_id);
                 if (codec) {
+                    if(codec->capabilities & CODEC_CAP_TRUNCATED)
+                        st->codec.flags |= CODEC_FLAG_TRUNCATED;
+
                     ret = avcodec_open(&st->codec, codec);
                     if (ret >= 0)
                         st->codec_info_state = CSTATE_DECODING;
@@ -757,23 +712,92 @@ AVStream *av_new_stream(AVFormatContext *s, int id)
  */
 int av_write_header(AVFormatContext *s)
 {
+    int ret, i;
+    AVStream *st;
+
     s->priv_data = av_mallocz(s->oformat->priv_data_size);
     if (!s->priv_data)
         return AVERROR_NOMEM;
-    return s->oformat->write_header(s);
+    /* default pts settings is MPEG like */
+    av_set_pts_info(s, 33, 1, 90000);
+    ret = s->oformat->write_header(s);
+    if (ret < 0)
+        return ret;
+
+    /* init PTS generation */
+    for(i=0;i<s->nb_streams;i++) {
+        st = s->streams[i];
+
+        switch (st->codec.codec_type) {
+        case CODEC_TYPE_AUDIO:
+            av_frac_init(&st->pts, 0, 0, 
+                         (INT64)s->pts_num * st->codec.sample_rate);
+            break;
+        case CODEC_TYPE_VIDEO:
+            av_frac_init(&st->pts, 0, 0, 
+                         (INT64)s->pts_num * st->codec.frame_rate);
+            break;
+        default:
+            break;
+        }
+    }
+    return 0;
 }
 
 /**
- * write a packet to an output media file
+ * Write a packet to an output media file. The packet shall contain
+ * one audio or video frame.
  *
  * @param s media file handle
- * @param pkt packet to write
- * @param force_pts XXX: need to suppress that
+ * @param stream_index stream index
+ * @param buf buffer containing the frame data
+ * @param size size of buffer
+ * @return < 0 if error, = 0 if OK, 1 if end of stream wanted.
  */
-int av_write_packet(AVFormatContext *s, AVPacket *pkt, int force_pts)
+int av_write_frame(AVFormatContext *s, int stream_index, const uint8_t *buf, 
+                   int size)
 {
-    /* XXX: currently, an emulation because internal API must change */
-    return s->oformat->write_packet(s, pkt->stream_index, pkt->data, pkt->size, force_pts);
+    AVStream *st;
+    INT64 pts_mask;
+    int ret, frame_size;
+
+    st = s->streams[stream_index];
+    pts_mask = (1LL << s->pts_wrap_bits) - 1;
+    ret = s->oformat->write_packet(s, stream_index, (uint8_t *)buf, size, 
+                                   st->pts.val & pts_mask);
+    if (ret < 0)
+        return ret;
+
+    /* update pts */
+    switch (st->codec.codec_type) {
+    case CODEC_TYPE_AUDIO:
+        if (st->codec.frame_size <= 1) {
+            frame_size = size / st->codec.channels;
+            /* specific hack for pcm codecs because no frame size is provided */
+            switch(st->codec.codec_id) {
+            case CODEC_ID_PCM_S16LE:
+            case CODEC_ID_PCM_S16BE:
+            case CODEC_ID_PCM_U16LE:
+            case CODEC_ID_PCM_U16BE:
+                frame_size >>= 1;
+                break;
+            default:
+                break;
+            }
+        } else {
+            frame_size = st->codec.frame_size;
+        }
+        av_frac_add(&st->pts, 
+                    (INT64)s->pts_den * frame_size);
+        break;
+    case CODEC_TYPE_VIDEO:
+        av_frac_add(&st->pts, 
+                    (INT64)s->pts_den * FRAME_RATE_BASE);
+        break;
+    default:
+        break;
+    }
+    return ret;
 }
 
 /**
@@ -862,7 +886,7 @@ int parse_image_size(int *width_ptr, int *height_ptr, const char *str)
     return 0;
 }
 
-INT64 gettime(void)
+INT64 av_gettime(void)
 {
 #ifdef CONFIG_WIN32
     struct _timeb tb;
@@ -875,60 +899,129 @@ INT64 gettime(void)
 #endif
 }
 
-/* syntax: [YYYY-MM-DD ][[HH:]MM:]SS[.m...] . Return the date in micro seconds since 1970 */
+static time_t mktimegm(struct tm *tm)
+{
+    time_t t;
+
+    int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
+
+    if (m < 3) {
+        m += 12;
+        y--;
+    }
+
+    t = 86400 * 
+        (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
+
+    t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
+
+    return t;
+}
+
+/* Syntax:
+ * - If not a duration:
+ *  [{YYYY-MM-DD|YYYYMMDD}]{T| }{HH[:MM[:SS[.m...]]][Z]|HH[MM[SS[.m...]]][Z]}
+ * Time is localtime unless Z is suffixed to the end. In this case GMT
+ * Return the date in micro seconds since 1970 
+ * - If duration:
+ *  HH[:MM[:SS[.m...]]]
+ *  S+[.m...]
+ */
 INT64 parse_date(const char *datestr, int duration)
 {
     const char *p;
     INT64 t;
     struct tm dt;
+    int i;
+    static const char *date_fmt[] = {
+        "%Y-%m-%d",
+        "%Y%m%d",
+    };
+    static const char *time_fmt[] = {
+        "%H:%M:%S",
+        "%H%M%S",
+    };
+    const char *q;
+    int is_utc, len;
+    char lastch;
+    time_t now = time(0);
+
+    len = strlen(datestr);
+    if (len > 0)
+        lastch = datestr[len - 1];
+    else
+        lastch = '\0';
+    is_utc = (lastch == 'z' || lastch == 'Z');
 
     memset(&dt, 0, sizeof(dt));
 
     p = datestr;
+    q = NULL;
     if (!duration) {
-        if (strlen(p) >= 5 && p[4] == '-') {
-            dt.tm_year = strtol(p, (char **)&p, 10);
-            if (*p)
-                p++;
-            dt.tm_mon = strtol(p, (char **)&p, 10) - 1;
-            if (*p)
-                p++;
-            dt.tm_mday = strtol(p, (char **)&p, 10) - 1;
-            if (*p)
-                p++;
+        for (i = 0; i < sizeof(date_fmt) / sizeof(date_fmt[0]); i++) {
+            q = strptime(p, date_fmt[i], &dt);
+            if (q) {
+                break;
+            }
+        }
+
+        if (!q) {
+            if (is_utc) {
+                dt = *gmtime(&now);
+            } else {
+                dt = *localtime(&now);
+            }
+            dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
         } else {
-            time_t now = time(0);
-            dt = *localtime(&now);
-            dt.tm_hour = 0;
+            p = q;
+        }
+
+        if (*p == 'T' || *p == 't' || *p == ' ')
+            p++;
+
+        for (i = 0; i < sizeof(time_fmt) / sizeof(time_fmt[0]); i++) {
+            q = strptime(p, time_fmt[i], &dt);
+            if (q) {
+                break;
+            }
+        }
+    } else {
+        q = strptime(p, time_fmt[0], &dt);
+        if (!q) {
+            dt.tm_sec = strtol(p, (char **)&q, 10);
             dt.tm_min = 0;
-            dt.tm_sec = 0;
+            dt.tm_hour = 0;
         }
     }
-    
-    dt.tm_hour = strtol(p, (char **)&p, 10);
-    if (*p)
-        p++;
-    dt.tm_min = strtol(p, (char **)&p, 10);
-    if (*p)
-        p++;
-    dt.tm_sec = strtol(p, (char **)&p, 10);
+
+    /* Now we have all the fields that we can get */
+    if (!q) {
+        if (duration)
+            return 0;
+        else
+            return now * INT64_C(1000000);
+    }
 
     if (duration) {
-        t = (INT64) 1000000 * (dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec);
+        t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
     } else {
-        t = (INT64) 1000000 * mktime(&dt);
+        dt.tm_isdst = -1;       /* unknown */
+        if (is_utc) {
+            t = mktimegm(&dt);
+        } else {
+            t = mktime(&dt);
+        }
     }
 
-    if (*p == '.') {
+    t *= 1000000;
+
+    if (*q == '.') {
         int val, n;
-        p++;
-        n = strlen(p);
-        if (n > 6)
-            n = 6;
-        val = strtol(p, NULL, 10);
-        while (n < 6) {
-            val = val * 10;
-            n++;
+        q++;
+        for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
+            if (!isdigit(*q)) 
+                break;
+            val += n * (*q - '0');
         }
         t += val;
     }
@@ -1031,35 +1124,6 @@ int get_frame_filename(char *buf, int buf_size,
     return -1;
 }
 
-static int gcd(INT64 a, INT64 b)
-{
-    INT64 c;
-
-    while (1) {
-        c = a % b;
-        if (c == 0)
-            return b;
-        a = b;
-        b = c;
-    }
-}
-
-void ticker_init(Ticker *tick, INT64 inrate, INT64 outrate)
-{
-    int g;
-
-    g = gcd(inrate, outrate);
-    inrate /= g;
-    outrate /= g;
-
-    tick->value = -outrate/2;
-
-    tick->inrate = inrate;
-    tick->outrate = outrate;
-    tick->div = tick->outrate / tick->inrate;
-    tick->mod = tick->outrate % tick->inrate;
-}
-
 /**
  *
  * Print on stdout a nice hexa dump of a buffer
@@ -1143,3 +1207,74 @@ void url_split(char *proto, int proto_size,
     pstrcpy(path, path_size, p);
 }
 
+/**
+ * Set the pts for a given stream
+ * @param s stream 
+ * @param pts_wrap_bits number of bits effectively used by the pts
+ *        (used for wrap control, 33 is the value for MPEG) 
+ * @param pts_num numerator to convert to seconds (MPEG: 1) 
+ * @param pts_den denominator to convert to seconds (MPEG: 90000)
+ */
+void av_set_pts_info(AVFormatContext *s, int pts_wrap_bits,
+                     int pts_num, int pts_den)
+{
+    s->pts_wrap_bits = pts_wrap_bits;
+    s->pts_num = pts_num;
+    s->pts_den = pts_den;
+}
+
+/* fraction handling */
+
+/**
+ * f = val + (num / den) + 0.5. 'num' is normalized so that it is such
+ * as 0 <= num < den.
+ *
+ * @param f fractional number
+ * @param val integer value
+ * @param num must be >= 0
+ * @param den must be >= 1 
+ */
+void av_frac_init(AVFrac *f, INT64 val, INT64 num, INT64 den)
+{
+    num += (den >> 1);
+    if (num >= den) {
+        val += num / den;
+        num = num % den;
+    }
+    f->val = val;
+    f->num = num;
+    f->den = den;
+}
+
+/* set f to (val + 0.5) */
+void av_frac_set(AVFrac *f, INT64 val)
+{
+    f->val = val;
+    f->num = f->den >> 1;
+}
+
+/**
+ * Fractionnal addition to f: f = f + (incr / f->den)
+ *
+ * @param f fractional number
+ * @param incr increment, can be positive or negative
+ */
+void av_frac_add(AVFrac *f, INT64 incr)
+{
+    INT64 num, den;
+
+    num = f->num + incr;
+    den = f->den;
+    if (num < 0) {
+        f->val += num / den;
+        num = num % den;
+        if (num < 0) {
+            num += den;
+            f->val--;
+        }
+    } else if (num >= den) {
+        f->val += num / den;
+        num = num % den;
+    }
+    f->num = num;
+}