From d4f5d74a54183a0198053b0ceb0faa21ad686551 Mon Sep 17 00:00:00 2001 From: Garrick Meeker Date: Wed, 9 Jul 2003 23:10:59 +0000 Subject: [PATCH] flash video (flv) support patch by (Garrick Meeker ) Originally committed as revision 2024 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavcodec/avcodec.c | 1 + libavcodec/avcodec.h | 3 + libavcodec/h263.c | 276 ++++++++++++++++++++++++++-- libavcodec/h263dec.c | 16 ++ libavcodec/imgconvert.c | 34 ++-- libavcodec/mpegvideo.c | 34 +++- libavcodec/mpegvideo.h | 2 + libavcodec/utils.c | 1 + libavformat/Makefile | 2 +- libavformat/allformats.c | 2 + libavformat/avformat.h | 6 + libavformat/flvdec.c | 156 ++++++++++++++++ libavformat/flvenc.c | 381 +++++++++++++++++++++++++++++++++++++++ 13 files changed, 885 insertions(+), 29 deletions(-) create mode 100644 libavformat/flvdec.c create mode 100644 libavformat/flvenc.c diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index 4f4114cda9..4f687568b7 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -39,6 +39,7 @@ static AVCodec* avcodec_find_by_fcc(uint32_t fcc) { CODEC_ID_MPEG1VIDEO, { MKTAG('P', 'I', 'M', '1'), 0 } }, { CODEC_ID_AC3, { 0x2000, 0 } }, { CODEC_ID_MP2, { 0x50, 0x55, 0 } }, + { CODEC_ID_FLV1, { MKTAG('F', 'L', 'V', '1'), 0 } }, { CODEC_ID_NONE, {0}} }; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 03bf032317..0ccc77cd27 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -41,6 +41,7 @@ enum CodecID { CODEC_ID_WMV2, CODEC_ID_H263P, CODEC_ID_H263I, + CODEC_ID_FLV1, CODEC_ID_SVQ1, CODEC_ID_SVQ3, CODEC_ID_DVVIDEO, @@ -1226,6 +1227,7 @@ extern AVCodec oggvorbis_encoder; extern AVCodec mpeg1video_encoder; extern AVCodec h263_encoder; extern AVCodec h263p_encoder; +extern AVCodec flv_encoder; extern AVCodec rv10_encoder; extern AVCodec mjpeg_encoder; extern AVCodec ljpeg_encoder; @@ -1249,6 +1251,7 @@ extern AVCodec wmv1_decoder; extern AVCodec wmv2_decoder; extern AVCodec mpeg_decoder; extern AVCodec h263i_decoder; +extern AVCodec flv_decoder; extern AVCodec rv10_decoder; extern AVCodec svq1_decoder; extern AVCodec svq3_decoder; diff --git a/libavcodec/h263.c b/libavcodec/h263.c index ac21677a70..e5b025e31a 100644 --- a/libavcodec/h263.c +++ b/libavcodec/h263.c @@ -50,11 +50,13 @@ #ifdef CONFIG_ENCODERS static void h263_encode_block(MpegEncContext * s, DCTELEM * block, - int n); + int n); +static void h263_flv_encode_block(MpegEncContext * s, DCTELEM * block, + int n); static void h263_encode_motion(MpegEncContext * s, int val, int fcode); static void h263p_encode_umotion(MpegEncContext * s, int val); static inline void mpeg4_encode_block(MpegEncContext * s, DCTELEM * block, - int n, int dc, uint8_t *scan_table, + int n, int dc, uint8_t *scan_table, PutBitContext *dc_pb, PutBitContext *ac_pb); #endif @@ -156,6 +158,48 @@ void h263_encode_picture_header(MpegEncContext * s, int picture_number) align_put_bits(&s->pb); + if (s->h263_flv) { + put_bits(&s->pb, 17, 1); + put_bits(&s->pb, 5, (s->h263_flv-1)); /* 0: h263 escape codes 1: 11-bit escape codes */ + put_bits(&s->pb, 8, (((int64_t)s->picture_number * 30 * s->avctx->frame_rate_base) / + s->avctx->frame_rate) & 0xff); /* TemporalReference */ + if (s->width == 352 && s->height == 288) + format = 2; + else if (s->width == 176 && s->height == 144) + format = 3; + else if (s->width == 128 && s->height == 96) + format = 4; + else if (s->width == 320 && s->height == 240) + format = 5; + else if (s->width == 160 && s->height == 120) + format = 6; + else if (s->width <= 255 && s->height <= 255) + format = 0; /* use 1 byte width & height */ + else + format = 1; /* use 2 bytes width & height */ + put_bits(&s->pb, 3, format); /* PictureSize */ + if (format == 0) { + put_bits(&s->pb, 8, s->width); + put_bits(&s->pb, 8, s->height); + } else if (format == 1) { + put_bits(&s->pb, 16, s->width); + put_bits(&s->pb, 16, s->height); + } + put_bits(&s->pb, 2, s->pict_type == P_TYPE); /* PictureType */ + put_bits(&s->pb, 1, 0); /* DeblockingFlag: off */ + put_bits(&s->pb, 5, s->qscale); /* Quantizer */ + put_bits(&s->pb, 1, 0); /* ExtraInformation */ + + if(s->h263_aic){ + s->y_dc_scale_table= + s->c_dc_scale_table= h263_aic_dc_scale_table; + }else{ + s->y_dc_scale_table= + s->c_dc_scale_table= ff_mpeg1_dc_scale_table; + } + return; + } + /* Update the pointer to last GOB */ s->ptr_lastgob = pbBufPtr(&s->pb); s->gob_number = 0; @@ -838,6 +882,7 @@ void h263_encode_mb(MpegEncContext * s, int16_t pred_dc; int16_t rec_intradc[6]; uint16_t *dc_ptr[6]; + const int interleaved_stats= (s->flags&CODEC_FLAG_PASS1); const int dquant_code[5]= {1,0,9,2,3}; //printf("**mb x=%d y=%d\n", s->mb_x, s->mb_y); @@ -851,6 +896,10 @@ void h263_encode_mb(MpegEncContext * s, if ((cbp | motion_x | motion_y | s->dquant) == 0) { /* skip macroblock */ put_bits(&s->pb, 1, 1); + if(interleaved_stats){ + s->misc_bits++; + s->last_bits++; + } return; } put_bits(&s->pb, 1, 0); /* mb coded */ @@ -865,6 +914,10 @@ void h263_encode_mb(MpegEncContext * s, if(s->dquant) put_bits(&s->pb, 2, dquant_code[s->dquant+2]); + if(interleaved_stats){ + s->misc_bits+= get_bits_diff(s); + } + /* motion vectors: 16x16 mode only now */ h263_pred_motion(s, 0, &pred_x, &pred_y); @@ -879,6 +932,10 @@ void h263_encode_mb(MpegEncContext * s, /* To prevent Start Code emulation */ put_bits(&s->pb,1,1); } + + if(interleaved_stats){ + s->mv_bits+= get_bits_diff(s); + } } else { int li = s->h263_aic ? 0 : 1; @@ -946,11 +1003,18 @@ void h263_encode_mb(MpegEncContext * s, put_bits(&s->pb, cbpy_tab[cbpy][1], cbpy_tab[cbpy][0]); if(s->dquant) put_bits(&s->pb, 2, dquant_code[s->dquant+2]); + + if(interleaved_stats){ + s->misc_bits+= get_bits_diff(s); + } } for(i=0; i<6; i++) { /* encode each block */ - h263_encode_block(s, block[i], i); + if (s->h263_flv > 1) + h263_flv_encode_block(s, block[i], i); + else + h263_encode_block(s, block[i], i); /* Update INTRADC for decoding */ if (s->h263_aic && s->mb_intra) { @@ -958,6 +1022,16 @@ void h263_encode_mb(MpegEncContext * s, } } + + if(interleaved_stats){ + if (!s->mb_intra) { + s->p_tex_bits+= get_bits_diff(s); + s->f_count++; + }else{ + s->i_tex_bits+= get_bits_diff(s); + s->i_count++; + } + } } #endif @@ -1467,6 +1541,17 @@ void h263_encode_init(MpegEncContext *s) s->max_qcoeff= 127; break; //Note for mpeg4 & h263 the dc-scale table will be set per frame as needed later + case CODEC_ID_FLV1: + if (s->h263_flv > 1) { + s->min_qcoeff= -1023; + s->max_qcoeff= 1023; + } else { + s->min_qcoeff= -127; + s->max_qcoeff= 127; + } + s->y_dc_scale_table= + s->c_dc_scale_table= ff_mpeg1_dc_scale_table; + break; default: //nothing needed default table allready set in mpegvideo.c s->min_qcoeff= -127; s->max_qcoeff= 127; @@ -1547,6 +1632,83 @@ static void h263_encode_block(MpegEncContext * s, DCTELEM * block, int n) } } } + +/** + * encodes a 8x8 block. + * @param block the 8x8 block + * @param n block index (0-3 are luma, 4-5 are chroma) + */ +static void h263_flv_encode_block(MpegEncContext * s, DCTELEM * block, int n) +{ + int level, run, last, i, j, last_index, last_non_zero, sign, slevel, code; + RLTable *rl; + + rl = &rl_inter; + if (s->mb_intra && !s->h263_aic) { + /* DC coef */ + level = block[0]; + /* 255 cannot be represented, so we clamp */ + if (level > 254) { + level = 254; + block[0] = 254; + } + /* 0 cannot be represented also */ + else if (level < 1) { + level = 1; + block[0] = 1; + } + if (level == 128) //FIXME check rv10 + put_bits(&s->pb, 8, 0xff); + else + put_bits(&s->pb, 8, level & 0xff); + i = 1; + } else { + i = 0; + if (s->h263_aic && s->mb_intra) + rl = &rl_intra_aic; + } + + /* AC coefs */ + last_index = s->block_last_index[n]; + last_non_zero = i - 1; + for (; i <= last_index; i++) { + j = s->intra_scantable.permutated[i]; + level = block[j]; + if (level) { + run = i - last_non_zero - 1; + last = (i == last_index); + sign = 0; + slevel = level; + if (level < 0) { + sign = 1; + level = -level; + } + code = get_rl_index(rl, last, run, level); + put_bits(&s->pb, rl->table_vlc[code][1], rl->table_vlc[code][0]); + if (code == rl->n) { + assert(slevel != 0); + if(slevel < 64 && slevel > -64) { + /* 7-bit level */ + put_bits(&s->pb, 1, 0); + put_bits(&s->pb, 1, last); + put_bits(&s->pb, 6, run); + + put_bits(&s->pb, 7, slevel & 0x7f); + } else { + /* 11-bit level */ + put_bits(&s->pb, 1, 1); + put_bits(&s->pb, 1, last); + put_bits(&s->pb, 6, run); + + put_bits(&s->pb, 11, slevel & 0x7ff); + } + } else { + put_bits(&s->pb, 1, sign); + } + last_non_zero = i; + } + } +} #endif #ifdef CONFIG_ENCODERS @@ -3553,16 +3715,27 @@ static int h263_decode_block(MpegEncContext * s, DCTELEM * block, } if (code == rl->n) { /* escape */ - last = get_bits1(&s->gb); - run = get_bits(&s->gb, 6); - level = (int8_t)get_bits(&s->gb, 8); - if(level == -128){ - if (s->h263_rv10) { - /* XXX: should patch encoder too */ - level = get_sbits(&s->gb, 12); - }else{ - level = get_bits(&s->gb, 5); - level |= get_sbits(&s->gb, 6)<<5; + if (s->h263_flv > 1) { + int is11 = get_bits1(&s->gb); + last = get_bits1(&s->gb); + run = get_bits(&s->gb, 6); + if(is11){ + level = (int8_t)get_sbits(&s->gb, 11); + } else { + level = (int8_t)get_sbits(&s->gb, 7); + } + } else { + last = get_bits1(&s->gb); + run = get_bits(&s->gb, 6); + level = (int8_t)get_bits(&s->gb, 8); + if(level == -128){ + if (s->h263_rv10) { + /* XXX: should patch encoder too */ + level = get_sbits(&s->gb, 12); + }else{ + level = get_bits(&s->gb, 5); + level |= get_sbits(&s->gb, 6)<<5; + } } } } else { @@ -4995,3 +5168,80 @@ int intel_h263_decode_picture_header(MpegEncContext *s) return 0; } +int flv_h263_decode_picture_header(MpegEncContext *s) +{ + int format, width, height; + + /* picture header */ + if (get_bits_long(&s->gb, 17) != 1) { + fprintf(stderr, "Bad picture start code\n"); + return -1; + } + format = get_bits(&s->gb, 5); + if (format != 0 && format != 1) { + fprintf(stderr, "Bad picture format\n"); + return -1; + } + s->h263_flv = format+1; + s->picture_number = get_bits(&s->gb, 8); /* picture timestamp */ + format = get_bits(&s->gb, 3); + switch (format) { + case 0: + width = get_bits(&s->gb, 8); + height = get_bits(&s->gb, 8); + break; + case 1: + width = get_bits(&s->gb, 16); + height = get_bits(&s->gb, 16); + break; + case 2: + width = 352; + height = 288; + break; + case 3: + width = 176; + height = 144; + break; + case 4: + width = 128; + height = 96; + break; + case 5: + width = 320; + height = 240; + break; + case 6: + width = 160; + height = 120; + break; + default: + width = height = 0; + break; + } + if ((width == 0) || (height == 0)) + return -1; + s->width = width; + s->height = height; + + s->pict_type = I_TYPE + get_bits(&s->gb, 2); + if (s->pict_type > P_TYPE) + s->pict_type = P_TYPE; + skip_bits1(&s->gb); /* deblocking flag */ + s->qscale = get_bits(&s->gb, 5); + + s->h263_plus = 0; + + s->unrestricted_mv = 1; + s->h263_long_vectors = s->unrestricted_mv; + + /* PEI */ + while (get_bits1(&s->gb) != 0) { + skip_bits(&s->gb, 8); + } + s->f_code = 1; + + s->y_dc_scale_table= + s->c_dc_scale_table= ff_mpeg1_dc_scale_table; + + return 0; +} diff --git a/libavcodec/h263dec.c b/libavcodec/h263dec.c index b1eb6fd877..33e21f5a57 100644 --- a/libavcodec/h263dec.c +++ b/libavcodec/h263dec.c @@ -96,6 +96,9 @@ int ff_h263_decode_init(AVCodecContext *avctx) case CODEC_ID_H263I: s->h263_intel = 1; break; + case CODEC_ID_FLV1: + s->h263_flv = 1; + break; default: return -1; } @@ -451,6 +454,8 @@ retry: s->low_delay=1; } else if (s->h263_intel) { ret = intel_h263_decode_picture_header(s); + } else if (s->h263_flv) { + ret = flv_h263_decode_picture_header(s); } else { ret = h263_decode_picture_header(s); } @@ -793,3 +798,14 @@ AVCodec h263i_decoder = { mpeg4_decoptions, }; +AVCodec flv_decoder = { + "flv", + CODEC_TYPE_VIDEO, + CODEC_ID_FLV1, + sizeof(MpegEncContext), + ff_h263_decode_init, + NULL, + ff_h263_decode_end, + ff_h263_decode_frame, + CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_DR1 +}; diff --git a/libavcodec/imgconvert.c b/libavcodec/imgconvert.c index 6a36c85589..4050ae7f3f 100644 --- a/libavcodec/imgconvert.c +++ b/libavcodec/imgconvert.c @@ -593,19 +593,19 @@ static void yuv422_to_yuv420p(AVPicture *dst, AVPicture *src, { const uint8_t *p, *p1; uint8_t *lum, *cr, *cb, *lum1, *cr1, *cb1; - int x; + int w; p1 = src->data[0]; lum1 = dst->data[0]; cb1 = dst->data[1]; cr1 = dst->data[2]; - for(;height >= 2; height -= 2) { + for(;height >= 1; height -= 2) { p = p1; lum = lum1; cb = cb1; cr = cr1; - for(x=0;x= 2; w -= 2) { lum[0] = p[0]; cb[0] = p[1]; lum[1] = p[2]; @@ -615,18 +615,30 @@ static void yuv422_to_yuv420p(AVPicture *dst, AVPicture *src, cb++; cr++; } - p1 += src->linesize[0]; - lum1 += dst->linesize[0]; - p = p1; - lum = lum1; - for(x=0;xlinesize[0]; lum1 += dst->linesize[0]; + if (height>1) { + p = p1; + lum = lum1; + for(w = width; w >= 2; w -= 2) { + lum[0] = p[0]; + lum[1] = p[2]; + p += 4; + lum += 2; + } + if (w) { + lum[0] = p[0]; + } + p1 += src->linesize[0]; + lum1 += dst->linesize[0]; + } cb1 += dst->linesize[1]; cr1 += dst->linesize[2]; } diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index 927e973c0e..8b9caaf802 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -670,6 +670,14 @@ int MPV_encode_init(AVCodecContext *avctx) avctx->delay=0; s->low_delay=1; break; + case CODEC_ID_FLV1: + s->out_format = FMT_H263; + s->h263_flv = 2; /* format = 1; 11-bit codes */ + s->unrestricted_mv = 1; + s->rtp_mode=0; /* don't allow GOB */ + avctx->delay=0; + s->low_delay=1; + break; case CODEC_ID_RV10: s->out_format = FMT_H263; s->h263_rv10 = 1; @@ -2997,6 +3005,7 @@ static void encode_mb(MpegEncContext *s, int motion_x, int motion_y) ff_wmv2_encode_mb(s, s->block, motion_x, motion_y); break; case CODEC_ID_H263: case CODEC_ID_H263P: + case CODEC_ID_FLV1: case CODEC_ID_RV10: h263_encode_mb(s, s->block, motion_x, motion_y); break; #endif @@ -3348,6 +3357,7 @@ static void encode_picture(MpegEncContext *s, int picture_number) break; case CODEC_ID_H263: case CODEC_ID_H263P: + case CODEC_ID_FLV1: ff_clean_h263_qscales(s); break; } @@ -3427,11 +3437,17 @@ static void encode_picture(MpegEncContext *s, int picture_number) s->last_mv_dir = 0; #ifdef CONFIG_RISKY - if (s->codec_id==CODEC_ID_H263 || s->codec_id==CODEC_ID_H263P) + switch(s->codec_id){ + case CODEC_ID_H263: + case CODEC_ID_H263P: + case CODEC_ID_FLV1: s->gob_index = ff_h263_get_gob_height(s); - - if(s->codec_id==CODEC_ID_MPEG4 && s->partitioned_frame) - ff_mpeg4_init_partitions(s); + break; + case CODEC_ID_MPEG4: + if(s->partitioned_frame) + ff_mpeg4_init_partitions(s); + break; + } #endif s->resync_mb_x=0; @@ -4439,6 +4455,16 @@ AVCodec h263p_encoder = { MPV_encode_end, }; +AVCodec flv_encoder = { + "flv", + CODEC_TYPE_VIDEO, + CODEC_ID_FLV1, + sizeof(MpegEncContext), + MPV_encode_init, + MPV_encode_picture, + MPV_encode_end, +}; + AVCodec rv10_encoder = { "rv10", CODEC_TYPE_VIDEO, diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index 5f3c10d243..812c5aa334 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -269,6 +269,7 @@ typedef struct MpegEncContext { int h263_rv10; ///< use RV10 variation for H263 int h263_msmpeg4; ///< generate MSMPEG4 compatible stream (deprecated, use msmpeg4_version instead) int h263_intel; ///< use I263 intel h263 header + int h263_flv; ///< use flv h263 header int codec_id; /* see CODEC_ID_xxx */ int fixed_qscale; ///< fixed qscale if non zero @@ -833,6 +834,7 @@ int ff_mpeg4_decode_picture_header(MpegEncContext * s, GetBitContext *gb); int intel_h263_decode_picture_header(MpegEncContext *s); +int flv_h263_decode_picture_header(MpegEncContext *s); int ff_h263_decode_mb(MpegEncContext *s, DCTELEM block[6][64]); int h263_get_picture_format(int width, int height); diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 3a30e734e4..5c25463747 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -610,6 +610,7 @@ void avcodec_flush_buffers(AVCodecContext *avctx) case CODEC_ID_WMV2: case CODEC_ID_H263P: case CODEC_ID_H263I: + case CODEC_ID_FLV1: case CODEC_ID_SVQ1: for(i=0; ipicture[i].data[0] && ( s->picture[i].type == FF_BUFFER_TYPE_INTERNAL diff --git a/libavformat/Makefile b/libavformat/Makefile index 647e687844..e5aa9c4a42 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -14,7 +14,7 @@ PPOBJS= # mux and demuxes OBJS+=mpeg.o mpegts.o mpegtsenc.o ffm.o crc.o img.o raw.o rm.o \ avienc.o avidec.o wav.o swf.o au.o gif.o mov.o mpjpeg.o dv.o \ - yuv4mpeg.o 4xm.o + yuv4mpeg.o 4xm.o flvenc.o flvdec.o ifeq ($(CONFIG_RISKY),yes) OBJS+= asf.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index f2d37656df..95d440f288 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -48,6 +48,8 @@ void av_register_all(void) jpeg_init(); dv_init(); fourxm_init(); + flvenc_init(); + flvdec_init(); #ifdef AMR_NB amr_init(); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index e4748bece2..052d642b96 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -304,6 +304,12 @@ int swf_init(void); /* mov.c */ int mov_init(void); +/* flvenc.c */ +int flvenc_init(void); + +/* flvdec.c */ +int flvdec_init(void); + /* jpeg.c */ int jpeg_init(void); diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c new file mode 100644 index 0000000000..b15e143c73 --- /dev/null +++ b/libavformat/flvdec.c @@ -0,0 +1,156 @@ +/* + * FLV encoder. + * Copyright (c) 2003 The FFmpeg Project. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "avformat.h" + +unsigned int get_be24(ByteIOContext *s) +{ + unsigned int val; + val = get_byte(s) << 16; + val |= get_byte(s) << 8; + val |= get_byte(s); + return val; +} + +static int flv_probe(AVProbeData *p) +{ + const uint8_t *d; + + if (p->buf_size < 6) + return 0; + d = p->buf; + if (d[0] == 'F' && d[1] == 'L' && d[2] == 'V') { + return 50; + } + return 0; +} + +static int flv_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + int offset, flags; + AVStream *st; + + av_set_pts_info(s, 24, 1, 1000); /* 24 bit pts in ms */ + + url_fskip(&s->pb, 4); + flags = get_byte(&s->pb); + + if ((flags & 1)) { + st = av_new_stream(s, 0); + if (!st) + return AVERROR_NOMEM; + st->codec.codec_type = CODEC_TYPE_VIDEO; + st->codec.codec_id = CODEC_ID_FLV1; + } + + if ((flags & 4)) { + st = av_new_stream(s, 1); + if (!st) + return AVERROR_NOMEM; + st->codec.codec_type = CODEC_TYPE_AUDIO; + st->codec.codec_id = CODEC_ID_MP3LAME; + } + + offset = get_be32(&s->pb); + url_fseek(&s->pb, offset, SEEK_SET); + + return 0; +} + +static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, i, type, size, pts, flags; + AVStream *st; + + redo: + url_fskip(&s->pb, 4); /* size of previous packet */ + type = get_byte(&s->pb); + size = get_be24(&s->pb); + pts = get_be24(&s->pb); + if (url_feof(&s->pb)) + return -EIO; + url_fskip(&s->pb, 4); /* reserved */ + flags = 0; + if (type == 8) { + flags = get_byte(&s->pb); + size--; + if ((flags >> 4) != 2) { /* 0: uncompressed 1: ADPCM 2: mp3 5: Nellymoser 8kHz mono 6: Nellymoser*/ + goto skip; + } + } else if (type == 9) { + flags = get_byte(&s->pb); + size--; + if ((flags & 0xF) != 2) { /* 2: only format */ + goto skip; + } + } else { + skip: + /* skip packet */ + printf("skipping flv packet: type %d, size %d, flags %d\n", type, size, flags); + url_fskip(&s->pb, size); + goto redo; + } + + /* now find stream */ + for(i=0;inb_streams;i++) { + st = s->streams[i]; + if (st->id == ((type == 9) ? 0 : 1)) + goto found; + } + goto skip; + found: + + if (av_new_packet(pkt, size) < 0) + return -EIO; + + ret = get_buffer(&s->pb, pkt->data, size); + if (ret <= 0) { + av_free_packet(pkt); + return -EIO; + } + /* note: we need to modify the packet size here to handle the last + packet */ + pkt->size = ret; + pkt->pts = pts; + pkt->stream_index = st->index; + return ret; +} + +static int flv_read_close(AVFormatContext *s) +{ + return 0; +} + +AVInputFormat flv_iformat = { + "flv", + "flv format", + 0, + flv_probe, + flv_read_header, + flv_read_packet, + flv_read_close, + .extensions = "flv", + .value = CODEC_ID_FLV1, +}; + +int flvdec_init(void) +{ + av_register_input_format(&flv_iformat); + return 0; +} diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c new file mode 100644 index 0000000000..b717bbb163 --- /dev/null +++ b/libavformat/flvenc.c @@ -0,0 +1,381 @@ +/* + * FLV encoder. + * Copyright (c) 2003 The FFmpeg Project. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "avformat.h" + +#define VIDEO_FIFO_SIZE 512 + +typedef struct FLVFrame { + int type; + int timestamp; + int flags; + uint8_t *data; + int size; + struct FLVFrame *next; +} FLVFrame; + +typedef struct FLVContext { + int hasAudio; + int hasVideo; +#ifdef CONFIG_MP3LAME + int audioTime; + int audioInPos; + int audioOutPos; + int audioSize; + int audioRate; + int initDelay; + int soundDelay; + uint8_t *audioFifo; + int64_t sampleCount; +#endif // CONFIG_MP3LAME + int64_t frameCount; + FLVFrame *frames; +} FLVContext; + + +#ifdef CONFIG_MP3LAME + +#define AUDIO_FIFO_SIZE 65536 + +static const int sSampleRates[3][4] = { + {44100, 48000, 32000, 0}, + {22050, 24000, 16000, 0}, + {11025, 12000, 8000, 0}, +}; + +static const int sBitRates[2][3][15] = { + { { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, + { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, + { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320} + }, + { { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160} + }, +}; + +static const int sSamplesPerFrame[3][3] = +{ + { 384, 1152, 1152 }, + { 384, 1152, 576 }, + { 384, 1152, 576 } +}; + +static const int sBitsPerSlot[3] = { + 32, + 8, + 8 +}; + +static int mp3info(void *data, int *byteSize, int *samplesPerFrame, int *sampleRate, int *isMono ) +{ + uint8_t *dataTmp = (uint8_t *)data; + uint32_t header = ( (uint32_t)dataTmp[0] << 24 ) | ( (uint32_t)dataTmp[1] << 16 ) | ( (uint32_t)dataTmp[2] << 8 ) | (uint32_t)dataTmp[3]; + int layerID = 3 - ((header >> 17) & 0x03); + int bitRateID = ((header >> 12) & 0x0f); + int sampleRateID = ((header >> 10) & 0x03); + int bitRate = 0; + int bitsPerSlot = sBitsPerSlot[layerID]; + int isPadded = ((header >> 9) & 0x01); + + if ( (( header >> 21 ) & 0x7ff) != 0x7ff ) { + return 0; + } + + if ( !isPadded ) { + printf("Fatal error: mp3 data is not padded!\n"); + exit(0); + } + + *isMono = ((header >> 6) & 0x03) == 0x03; + + if ( (header >> 19 ) & 0x01 ) { + *sampleRate = sSampleRates[0][sampleRateID]; + bitRate = sBitRates[0][layerID][bitRateID] * 1000; + *samplesPerFrame = sSamplesPerFrame[0][layerID]; + + } else { + if ( (header >> 20) & 0x01 ) { + *sampleRate = sSampleRates[1][sampleRateID]; + bitRate = sBitRates[1][layerID][bitRateID] * 1000; + *samplesPerFrame = sSamplesPerFrame[1][layerID]; + } else { + *sampleRate = sSampleRates[2][sampleRateID]; + bitRate = sBitRates[1][layerID][bitRateID] * 1000; + *samplesPerFrame = sSamplesPerFrame[2][layerID]; + } + } + + *byteSize = ( ( ( ( *samplesPerFrame * (bitRate / bitsPerSlot) ) / *sampleRate ) + isPadded ) * bitsPerSlot); + + return 1; +} +#endif // CONFIG_MP3LAME + +static int flv_write_header(AVFormatContext *s) +{ + ByteIOContext *pb = &s->pb; + FLVContext *flv = s->priv_data; + + av_set_pts_info(s, 24, 1, 1000); /* 24 bit pts in ms */ + + flv->hasAudio = 0; + flv->hasVideo = 0; + +#ifdef CONFIG_MP3LAME + flv->audioTime = -1; + flv->audioFifo = av_malloc(AUDIO_FIFO_SIZE); + flv->audioInPos = 0; + flv->audioOutPos = 0; + flv->audioSize = 0; + flv->audioRate = 44100; + flv->initDelay = -1; + flv->soundDelay = 0; +#endif // CONFIG_MP3LAME + + flv->frames = 0; + + put_tag(pb,"FLV"); + put_byte(pb,1); + put_byte(pb,0); // delayed write + put_be32(pb,9); + put_be32(pb,0); + + return 0; +} + +static void put_be24(ByteIOContext *pb, int value) +{ + put_byte(pb, (value>>16) & 0xFF ); + put_byte(pb, (value>> 8) & 0xFF ); + put_byte(pb, (value>> 0) & 0xFF ); +} + +static void InsertSorted(FLVContext *flv, FLVFrame *frame) +{ + if ( !flv->frames ) { + flv->frames = frame; + } else { + FLVFrame *trav = flv->frames; + FLVFrame *prev = 0; + for (;trav;) { + if ( trav->timestamp >= frame->timestamp ) { + frame->next = trav; + if ( prev ) { + prev->next = frame; + } else { + flv->frames = frame; + } + break; + } + prev = trav; + trav = trav->next; + } + if ( !trav ) { + prev->next = frame; + } + } +} + +static void DumpFrame(ByteIOContext *pb, FLVFrame *frame) +{ + put_byte(pb,frame->type); // message type + put_be24(pb,frame->size+1); // include flags + put_be24(pb,frame->timestamp); // time stamp + put_be32(pb,0); // reserved + put_byte(pb,frame->flags); + put_buffer(pb, frame->data, frame->size); + put_be32(pb,frame->size+1+11); // reserved + av_free(frame->data); +} + +static void Dump(FLVContext *flv, ByteIOContext *pb, int count) +{ + int c=0; + FLVFrame *trav = flv->frames; + FLVFrame *prev = 0; + for (;trav;c++) { + trav = trav->next; + } + trav = flv->frames; + for ( ; c >= count; c-- ) { + DumpFrame(pb,trav); + prev = trav; + trav = trav->next; + av_free(prev); + } + flv->frames = trav; +} + +static int flv_write_trailer(AVFormatContext *s) +{ + ByteIOContext *pb = &s->pb; + FLVContext *flv = s->priv_data; + + Dump(flv,pb,1); + + int64_t file_size = url_ftell(pb); + int flags = 0; + flags |= flv->hasAudio ? 4 : 0; + flags |= flv->hasVideo ? 1 : 0; + url_fseek(pb, 4, SEEK_SET); + put_byte(pb,flags); + url_fseek(pb, file_size, SEEK_SET); + return 0; +} + +static int flv_write_packet(AVFormatContext *s, int stream_index, + uint8_t *buf, int size, int timestamp) +{ + ByteIOContext *pb = &s->pb; + AVCodecContext *enc = &s->streams[stream_index]->codec; + FLVContext *flv = s->priv_data; + + if (enc->codec_type == CODEC_TYPE_VIDEO) { + FLVFrame *frame = av_malloc(sizeof(FLVFrame)); + frame->next = 0; + frame->type = 9; + frame->flags = 2; // choose h263 + frame->flags |= enc->coded_frame->key_frame ? 0x10 : 0x20; // add keyframe indicator + frame->timestamp = timestamp; + //frame->timestamp = ( ( flv->frameCount * (int64_t)FRAME_RATE_BASE * (int64_t)1000 ) / (int64_t)enc->frame_rate ); + //printf("%08x %f %f\n",frame->timestamp,(double)enc->frame_rate/(double)FRAME_RATE_BASE,1000*(double)FRAME_RATE_BASE/(double)enc->frame_rate); + frame->size = size; + frame->data = av_malloc(size); + memcpy(frame->data,buf,size); + flv->hasVideo = 1; + + InsertSorted(flv,frame); + + flv->frameCount ++; + } + else if (enc->codec_type == CODEC_TYPE_AUDIO) { +#ifdef CONFIG_MP3LAME + if (enc->codec_id == CODEC_ID_MP3LAME ) { + int c=0; + for (;caudioFifo[(flv->audioOutPos+c)%AUDIO_FIFO_SIZE] = buf[c]; + } + flv->audioSize += size; + flv->audioOutPos += size; + flv->audioOutPos %= AUDIO_FIFO_SIZE; + + if ( flv->initDelay == -1 ) { + flv->initDelay = timestamp; + } + + if ( flv->audioTime == -1 ) { + flv->audioTime = timestamp; +// flv->audioTime = ( ( ( flv->sampleCount - enc->delay ) * 8000 ) / flv->audioRate ) - flv->initDelay - 250; +// if ( flv->audioTime < 0 ) { +// flv->audioTime = 0; +// } + } + } + for ( ; flv->audioSize >= 4 ; ) { + + int mp3FrameSize = 0; + int mp3SampleRate = 0; + int mp3IsMono = 0; + int mp3SamplesPerFrame = 0; + + if ( mp3info(&flv->audioFifo[flv->audioInPos],&mp3FrameSize,&mp3SamplesPerFrame,&mp3SampleRate,&mp3IsMono) ) { + if ( flv->audioSize >= mp3FrameSize ) { + + int soundFormat = 0x22; + int c=0; + FLVFrame *frame = av_malloc(sizeof(FLVFrame)); + + flv->audioRate = mp3SampleRate; + + switch (mp3SampleRate) { + case 44100: + soundFormat |= 0x0C; + break; + case 22050: + soundFormat |= 0x08; + break; + case 11025: + soundFormat |= 0x04; + break; + } + + if ( !mp3IsMono ) { + soundFormat |= 0x01; + } + + frame->next = 0; + frame->type = 8; + frame->flags = soundFormat; + frame->timestamp = flv->audioTime; + frame->size = mp3FrameSize; + frame->data = av_malloc(mp3FrameSize); + + for (;cdata[c] = flv->audioFifo[(flv->audioInPos+c)%AUDIO_FIFO_SIZE]; + } + + flv->audioInPos += mp3FrameSize; + flv->audioSize -= mp3FrameSize; + flv->audioInPos %= AUDIO_FIFO_SIZE; + flv->sampleCount += mp3SamplesPerFrame; + + // Reset audio for next round + flv->audioTime = -1; + // We got audio! Make sure we set this to the global flags on closure + flv->hasAudio = 1; + + InsertSorted(flv,frame); + } + break; + } + flv->audioInPos ++; + flv->audioSize --; + flv->audioInPos %= AUDIO_FIFO_SIZE; + // no audio in here! + flv->audioTime = -1; + } +#endif + } + Dump(flv,pb,128); + put_flush_packet(pb); + return 0; +} + +static AVOutputFormat flv_oformat = { + "flv", + "flv format", + "video/x-flashvideo", + "flv", + sizeof(FLVContext), +#ifdef CONFIG_MP3LAME + CODEC_ID_MP3LAME, +#else // CONFIG_MP3LAME + CODEC_ID_NONE, +#endif // CONFIG_MP3LAME + CODEC_ID_FLV1, + flv_write_header, + flv_write_packet, + flv_write_trailer, +}; + +int flvenc_init(void) +{ + av_register_output_format(&flv_oformat); + return 0; +}