static int gxf_write_media_preamble(AVFormatContext *s, AVPacket *pkt, int size) { GXFContext *gxf = s->priv_data; AVIOContext *pb = s->pb; AVStream *st = s->streams[pkt->stream_index]; GXFStreamContext *sc = st->priv_data; unsigned field_nb; /* If the video is frame-encoded, the frame numbers shall be represented by * even field numbers. * see SMPTE360M-2004 6.4.2.1.3 Media field number */ if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { field_nb = gxf->nb_fields; } else { field_nb = av_rescale_rnd(pkt->dts, gxf->time_base.den, (int64_t)48000 * gxf->time_base.num, AV_ROUND_UP); } avio_w8(pb, sc->media_type); avio_w8(pb, st->index); avio_wb32(pb, field_nb); if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { avio_wb16(pb, 0); avio_wb16(pb, size / 2); } else if (st->codec->codec_id == CODEC_ID_MPEG2VIDEO) { int frame_type = gxf_parse_mpeg_frame(sc, pkt->data, pkt->size); if (frame_type == FF_I_TYPE) { avio_w8(pb, 0x0d); sc->iframes++; } else if (frame_type == FF_B_TYPE) { avio_w8(pb, 0x0f); sc->bframes++; } else { avio_w8(pb, 0x0e); sc->pframes++; } avio_wb24(pb, size); } else if (st->codec->codec_id == CODEC_ID_DVVIDEO) { avio_w8(pb, size / 4096); avio_wb24(pb, 0); } else avio_wb32(pb, size); avio_wb32(pb, field_nb); avio_w8(pb, 1); /* flags */ avio_w8(pb, 0); /* reserved */ return 16; }
static void put_avc_eos_tag(AVIOContext *pb, unsigned ts) { avio_w8(pb, FLV_TAG_TYPE_VIDEO); avio_wb24(pb, 5); /* Tag Data Size */ avio_wb24(pb, ts); /* lower 24 bits of timestamp in ms*/ avio_w8(pb, (ts >> 24) & 0x7F); /* MSB of ts in ms*/ avio_wb24(pb, 0); /* StreamId = 0 */ avio_w8(pb, 23); /* ub[4] FrameType = 1, ub[4] CodecId = 7 */ avio_w8(pb, 2); /* AVC end of sequence */ avio_wb24(pb, 0); /* Always 0 for AVC EOS. */ avio_wb32(pb, 16); /* Size of FLV tag */ }
static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_bytes, int last_block) { avio_w8(pb, last_block ? 0x81 : 0x01); avio_wb24(pb, n_padding_bytes); ffio_fill(pb, 0, n_padding_bytes); return 0; }
static int flac_write_block_padding(AVIOContext *pb, unsigned int n_padding_bytes, int last_block) { avio_w8(pb, last_block ? 0x81 : 0x01); avio_wb24(pb, n_padding_bytes); while (n_padding_bytes > 0) { avio_w8(pb, 0); n_padding_bytes--; } return 0; }
static int gif_image_write_header(AVFormatContext *s, int width, int height, int loop_count, uint32_t *palette) { AVIOContext *pb = s->pb; AVRational sar = s->streams[0]->codec->sample_aspect_ratio; int i; int64_t aspect = 0; if (sar.num > 0 && sar.den > 0) { aspect = sar.num * LLN(64) / sar.den - 15; if (aspect < 0 || aspect > 255) aspect = 0; } avio_write(pb, "GIF", 3); avio_write(pb, "89a", 3); avio_wl16(pb, width); avio_wl16(pb, height); if (palette) { avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ avio_w8(pb, 0x1f); /* background color index */ avio_w8(pb, aspect); for (i = 0; i < 256; i++) { const uint32_t v = palette[i] & 0xffffff; avio_wb24(pb, v); } } else { avio_w8(pb, 0); /* flags */ avio_w8(pb, 0); /* background color index */ avio_w8(pb, aspect); } if (loop_count >= 0 ) { /* "NETSCAPE EXTENSION" for looped animation GIF */ avio_w8(pb, 0x21); /* GIF Extension code */ avio_w8(pb, 0xff); /* Application Extension Label */ avio_w8(pb, 0x0b); /* Length of Application Block */ avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1); avio_w8(pb, 0x03); /* Length of Data Sub-Block */ avio_w8(pb, 0x01); avio_wl16(pb, (uint16_t)loop_count); avio_w8(pb, 0x00); /* Data Sub-block Terminator */ } return 0; }
static int gif_image_write_header(AVIOContext *pb, int width, int height, int loop_count, uint32_t *palette) { int i; avio_write(pb, "GIF", 3); avio_write(pb, "89a", 3); avio_wl16(pb, width); avio_wl16(pb, height); if (palette) { avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ avio_w8(pb, 0x1f); /* background color index */ avio_w8(pb, 0); /* aspect ratio */ for (i = 0; i < 256; i++) { const uint32_t v = palette[i] & 0xffffff; avio_wb24(pb, v); } } else { avio_w8(pb, 0); /* flags */ avio_w8(pb, 0); /* background color index */ avio_w8(pb, 0); /* aspect ratio */ } if (loop_count >= 0 ) { /* "NETSCAPE EXTENSION" for looped animation GIF */ avio_w8(pb, 0x21); /* GIF Extension code */ avio_w8(pb, 0xff); /* Application Extension Label */ avio_w8(pb, 0x0b); /* Length of Application Block */ avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1); avio_w8(pb, 0x03); /* Length of Data Sub-Block */ avio_w8(pb, 0x01); avio_wl16(pb, (uint16_t)loop_count); avio_w8(pb, 0x00); /* Data Sub-block Terminator */ } return 0; }
/* * Write an empty XING header and initialize respective data. */ static void mp3_write_xing(AVFormatContext *s) { MP3Context *mp3 = s->priv_data; AVCodecContext *codec = s->streams[mp3->audio_stream_idx]->codec; AVDictionaryEntry *enc = av_dict_get(s->streams[mp3->audio_stream_idx]->metadata, "encoder", NULL, 0); AVIOContext *dyn_ctx; int32_t header; MPADecodeHeader mpah; int srate_idx, i, channels; int bitrate_idx; int best_bitrate_idx; int best_bitrate_error = INT_MAX; int ret; int ver = 0; int lsf, bytes_needed; if (!s->pb->seekable || !mp3->write_xing) return; for (i = 0; i < FF_ARRAY_ELEMS(avpriv_mpa_freq_tab); i++) { const uint16_t base_freq = avpriv_mpa_freq_tab[i]; if (codec->sample_rate == base_freq) ver = 0x3; // MPEG 1 else if (codec->sample_rate == base_freq / 2) ver = 0x2; // MPEG 2 else if (codec->sample_rate == base_freq / 4) ver = 0x0; // MPEG 2.5 else continue; srate_idx = i; break; } if (i == FF_ARRAY_ELEMS(avpriv_mpa_freq_tab)) { av_log(s, AV_LOG_WARNING, "Unsupported sample rate, not writing Xing " "header.\n"); return; } switch (codec->channels) { case 1: channels = MPA_MONO; break; case 2: channels = MPA_STEREO; break; default: av_log(s, AV_LOG_WARNING, "Unsupported number of channels, " "not writing Xing header.\n"); return; } /* dummy MPEG audio header */ header = 0xff << 24; // sync header |= (0x7 << 5 | ver << 3 | 0x1 << 1 | 0x1) << 16; // sync/audio-version/layer 3/no crc*/ header |= (srate_idx << 2) << 8; header |= channels << 6; lsf = !((header & (1 << 20) && header & (1 << 19))); mp3->xing_offset = xing_offtbl[ver != 3][channels == 1] + 4; bytes_needed = mp3->xing_offset + XING_SIZE; for (bitrate_idx = 1; bitrate_idx < 15; bitrate_idx++) { int bit_rate = 1000 * avpriv_mpa_bitrate_tab[lsf][3 - 1][bitrate_idx]; int error = FFABS(bit_rate - codec->bit_rate); if (error < best_bitrate_error){ best_bitrate_error = error; best_bitrate_idx = bitrate_idx; } } for (bitrate_idx = best_bitrate_idx; bitrate_idx < 15; bitrate_idx++) { int32_t mask = bitrate_idx << (4 + 8); header |= mask; avpriv_mpegaudio_decode_header(&mpah, header); if (bytes_needed <= mpah.frame_size) break; header &= ~mask; } ret = avio_open_dyn_buf(&dyn_ctx); if (ret < 0) return; avio_wb32(dyn_ctx, header); avpriv_mpegaudio_decode_header(&mpah, header); av_assert0(mpah.frame_size >= bytes_needed); ffio_fill(dyn_ctx, 0, mp3->xing_offset - 4); ffio_wfourcc(dyn_ctx, "Xing"); avio_wb32(dyn_ctx, 0x01 | 0x02 | 0x04 | 0x08); // frames / size / TOC / vbr scale mp3->size = mpah.frame_size; mp3->want = 1; avio_wb32(dyn_ctx, 0); // frames avio_wb32(dyn_ctx, 0); // size // TOC for (i = 0; i < XING_TOC_SIZE; i++) avio_w8(dyn_ctx, 255 * i / XING_TOC_SIZE); // vbr quality // we write it, because some (broken) tools always expect it to be present avio_wb32(dyn_ctx, 0); // encoder short version string if (enc) { uint8_t encoder_str[9] = { 0 }; memcpy(encoder_str, enc->value, FFMIN(strlen(enc->value), sizeof(encoder_str))); avio_write(dyn_ctx, encoder_str, sizeof(encoder_str)); } else ffio_fill(dyn_ctx, 0, 9); avio_w8(dyn_ctx, 0); // tag revision 0 / unknown vbr method avio_w8(dyn_ctx, 0); // unknown lowpass filter value ffio_fill(dyn_ctx, 0, 8); // empty replaygain fields avio_w8(dyn_ctx, 0); // unknown encoding flags avio_w8(dyn_ctx, 0); // unknown abr/minimal bitrate // encoder delay if (codec->initial_padding >= 1 << 12) { av_log(s, AV_LOG_WARNING, "Too many samples of initial padding.\n"); avio_wb24(dyn_ctx, 0); } else { avio_wb24(dyn_ctx, codec->initial_padding << 12); } avio_w8(dyn_ctx, 0); // misc avio_w8(dyn_ctx, 0); // mp3gain avio_wb16(dyn_ctx, 0); // preset // audio length and CRCs (will be updated later) avio_wb32(dyn_ctx, 0); // music length avio_wb16(dyn_ctx, 0); // music crc avio_wb16(dyn_ctx, 0); // tag crc ffio_fill(dyn_ctx, 0, mpah.frame_size - bytes_needed); mp3->xing_frame_size = avio_close_dyn_buf(dyn_ctx, &mp3->xing_frame); mp3->xing_frame_offset = avio_tell(s->pb); avio_write(s->pb, mp3->xing_frame, mp3->xing_frame_size); mp3->audio_size = mp3->xing_frame_size; }
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; AVCodecContext *enc = s->streams[pkt->stream_index]->codec; FLVContext *flv = s->priv_data; unsigned ts; int size= pkt->size; uint8_t *data= NULL; int flags, flags_size; // av_log(s, AV_LOG_DEBUG, "type:%d pts: %"PRId64" size:%d\n", enc->codec_type, timestamp, size); if(enc->codec_id == CODEC_ID_VP6 || enc->codec_id == CODEC_ID_VP6F || enc->codec_id == CODEC_ID_AAC) flags_size= 2; else if(enc->codec_id == CODEC_ID_H264) flags_size= 5; else flags_size= 1; if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { avio_w8(pb, FLV_TAG_TYPE_VIDEO); flags = enc->codec_tag; if(flags == 0) { av_log(enc, AV_LOG_ERROR, "video codec %X not compatible with flv\n",enc->codec_id); return -1; } flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; } else { assert(enc->codec_type == AVMEDIA_TYPE_AUDIO); flags = get_audio_flags(enc); assert(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); } if (enc->codec_id == CODEC_ID_H264) { /* check if extradata looks like mp4 formated */ if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) { if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0) return -1; } if (!flv->delay && pkt->dts < 0) flv->delay = -pkt->dts; } ts = pkt->dts + flv->delay; // add delay to force positive dts if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { if (flv->last_video_ts < ts) flv->last_video_ts = ts; } avio_wb24(pb,size + flags_size); avio_wb24(pb,ts); avio_w8(pb,(ts >> 24) & 0x7F); // timestamps are 32bits _signed_ avio_wb24(pb,flv->reserved); avio_w8(pb,flags); if (enc->codec_id == CODEC_ID_VP6) avio_w8(pb,0); if (enc->codec_id == CODEC_ID_VP6F) avio_w8(pb, enc->extradata_size ? enc->extradata[0] : 0); else if (enc->codec_id == CODEC_ID_AAC) avio_w8(pb,1); // AAC raw else if (enc->codec_id == CODEC_ID_H264) { avio_w8(pb,1); // AVC NALU avio_wb24(pb,pkt->pts - pkt->dts); } avio_write(pb, data ? data : pkt->data, size); avio_wb32(pb,size+flags_size+11); // previous tag size flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); put_flush_packet(pb); av_free(data); return 0; }
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; AVCodecContext *enc = s->streams[pkt->stream_index]->codec; FLVContext *flv = s->priv_data; FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size = pkt->size; uint8_t *data = NULL; int flags = -1, flags_size, ret; if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A || enc->codec_id == AV_CODEC_ID_VP6 || enc->codec_id == AV_CODEC_ID_AAC) flags_size = 2; else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) flags_size = 5; else flags_size = 1; if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; if (pkt->dts < -flv->delay) { av_log(s, AV_LOG_WARNING, "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } ts = pkt->dts + flv->delay; // add delay to force positive dts if (s->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) { write_metadata(s, ts); s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED; } switch (enc->codec_type) { case AVMEDIA_TYPE_VIDEO: avio_w8(pb, FLV_TAG_TYPE_VIDEO); flags = enc->codec_tag; if (flags == 0) { av_log(s, AV_LOG_ERROR, "Video codec '%s' is not compatible with FLV\n", avcodec_get_name(enc->codec_id)); return AVERROR(EINVAL); } flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, enc); av_assert0(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); break; case AVMEDIA_TYPE_SUBTITLE: case AVMEDIA_TYPE_DATA: avio_w8(pb, FLV_TAG_TYPE_META); break; default: return AVERROR(EINVAL); } if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { /* check if extradata looks like mp4 formated */ if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) return ret; } else if (enc->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { if (!s->streams[pkt->stream_index]->nb_frames) { av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " "use the audio bitstream filter 'aac_adtstoasc' to fix it " "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); return AVERROR_INVALIDDATA; } av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); } /* check Speex packet duration */ if (enc->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160) av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); if (sc->last_ts < ts) sc->last_ts = ts; if (size + flags_size >= 1<<24) { av_log(s, AV_LOG_ERROR, "Too large packet with size %u >= %u\n", size + flags_size, 1<<24); return AVERROR(EINVAL); } avio_wb24(pb, size + flags_size); avio_wb24(pb, ts & 0xFFFFFF); avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_ avio_wb24(pb, flv->reserved); if (enc->codec_type == AVMEDIA_TYPE_DATA || enc->codec_type == AVMEDIA_TYPE_SUBTITLE ) { int data_size; int64_t metadata_size_pos = avio_tell(pb); if (enc->codec_id == AV_CODEC_ID_TEXT) { // legacy FFmpeg magic? avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onTextData"); avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); avio_wb32(pb, 2); put_amf_string(pb, "type"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "Text"); put_amf_string(pb, "text"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, pkt->data); put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); } else { // just pass the metadata through avio_write(pb, data ? data : pkt->data, size); } /* write total size of tag */ data_size = avio_tell(pb) - metadata_size_pos; avio_seek(pb, metadata_size_pos - 10, SEEK_SET); avio_wb24(pb, data_size); avio_seek(pb, data_size + 10 - 3, SEEK_CUR); avio_wb32(pb, data_size + 11); } else { av_assert1(flags>=0); avio_w8(pb,flags); if (enc->codec_id == AV_CODEC_ID_VP6) avio_w8(pb,0); if (enc->codec_id == AV_CODEC_ID_VP6F || enc->codec_id == AV_CODEC_ID_VP6A) { if (enc->extradata_size) avio_w8(pb, enc->extradata[0]); else avio_w8(pb, ((FFALIGN(enc->width, 16) - enc->width) << 4) | (FFALIGN(enc->height, 16) - enc->height)); } else if (enc->codec_id == AV_CODEC_ID_AAC) avio_w8(pb, 1); // AAC raw else if (enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { avio_w8(pb, 1); // AVC NALU avio_wb24(pb, pkt->pts - pkt->dts); } avio_write(pb, data ? data : pkt->data, size); avio_wb32(pb, size + flags_size + 11); // previous tag size flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); } av_free(data); return pb->error; }
static int flv_write_header(AVFormatContext *s) { int i; AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; int64_t data_size; for (i = 0; i < s->nb_streams; i++) { AVCodecContext *enc = s->streams[i]->codec; FLVStreamContext *sc; switch (enc->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->streams[i]->avg_frame_rate.den && s->streams[i]->avg_frame_rate.num) { flv->framerate = av_q2d(s->streams[i]->avg_frame_rate); } if (flv->video_enc) { av_log(s, AV_LOG_ERROR, "at most one video stream is supported in flv\n"); return AVERROR(EINVAL); } flv->video_enc = enc; if (enc->codec_tag == 0) { av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with FLV\n", avcodec_get_name(enc->codec_id), i); return AVERROR(EINVAL); } if (enc->codec_id == AV_CODEC_ID_MPEG4 || enc->codec_id == AV_CODEC_ID_H263) { int error = s->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL; av_log(s, error ? AV_LOG_ERROR : AV_LOG_WARNING, "Codec %s is not supported in the official FLV specification,\n", avcodec_get_name(enc->codec_id)); if (error) { av_log(s, AV_LOG_ERROR, "use vstrict=-1 / -strict -1 to use it anyway.\n"); return AVERROR(EINVAL); } } else if (enc->codec_id == AV_CODEC_ID_VP6) { av_log(s, AV_LOG_WARNING, "Muxing VP6 in flv will produce flipped video on playback.\n"); } break; case AVMEDIA_TYPE_AUDIO: if (flv->audio_enc) { av_log(s, AV_LOG_ERROR, "at most one audio stream is supported in flv\n"); return AVERROR(EINVAL); } flv->audio_enc = enc; if (get_audio_flags(s, enc) < 0) return AVERROR_INVALIDDATA; if (enc->codec_id == AV_CODEC_ID_PCM_S16BE) av_log(s, AV_LOG_WARNING, "16-bit big-endian audio in flv is valid but most likely unplayable (hardware dependent); use s16le\n"); break; case AVMEDIA_TYPE_DATA: if (enc->codec_id != AV_CODEC_ID_TEXT && enc->codec_id != AV_CODEC_ID_NONE) { av_log(s, AV_LOG_ERROR, "Data codec '%s' for stream %d is not compatible with FLV\n", avcodec_get_name(enc->codec_id), i); return AVERROR_INVALIDDATA; } flv->data_enc = enc; break; case AVMEDIA_TYPE_SUBTITLE: if (enc->codec_id != AV_CODEC_ID_TEXT) { av_log(s, AV_LOG_ERROR, "Subtitle codec '%s' for stream %d is not compatible with FLV\n", avcodec_get_name(enc->codec_id), i); return AVERROR_INVALIDDATA; } flv->data_enc = enc; break; default: av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with FLV\n", av_get_media_type_string(enc->codec_type), i); return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ sc = av_mallocz(sizeof(FLVStreamContext)); if (!sc) return AVERROR(ENOMEM); s->streams[i]->priv_data = sc; sc->last_ts = -1; } flv->delay = AV_NOPTS_VALUE; avio_write(pb, "FLV", 3); avio_w8(pb, 1); avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!flv->audio_enc + FLV_HEADER_FLAG_HASVIDEO * !!flv->video_enc); avio_wb32(pb, 9); avio_wb32(pb, 0); for (i = 0; i < s->nb_streams; i++) if (s->streams[i]->codec->codec_tag == 5) { avio_w8(pb, 8); // message type avio_wb24(pb, 0); // include flags avio_wb24(pb, 0); // time stamp avio_wb32(pb, 0); // reserved avio_wb32(pb, 11); // size flv->reserved = 5; } write_metadata(s, 0); for (i = 0; i < s->nb_streams; i++) { AVCodecContext *enc = s->streams[i]->codec; if (enc->codec_id == AV_CODEC_ID_AAC || enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { int64_t pos; avio_w8(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO ? FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); avio_wb24(pb, 0); // size patched later avio_wb24(pb, 0); // ts avio_w8(pb, 0); // ts ext avio_wb24(pb, 0); // streamid pos = avio_tell(pb); if (enc->codec_id == AV_CODEC_ID_AAC) { avio_w8(pb, get_audio_flags(s, enc)); avio_w8(pb, 0); // AAC sequence header avio_write(pb, enc->extradata, enc->extradata_size); } else { avio_w8(pb, enc->codec_tag | FLV_FRAME_KEY); // flags avio_w8(pb, 0); // AVC sequence header avio_wb24(pb, 0); // composition time ff_isom_write_avcc(pb, enc->extradata, enc->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); // previous tag size } } return 0; }
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; FLVContext *flv = s->priv_data; FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size = pkt->size; uint8_t *data = NULL; int flags = 0, flags_size; if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A || par->codec_id == AV_CODEC_ID_AAC) flags_size = 2; else if (par->codec_id == AV_CODEC_ID_H264) flags_size = 5; else flags_size = 1; if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; if (pkt->dts < -flv->delay) { av_log(s, AV_LOG_WARNING, "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } ts = pkt->dts + flv->delay; // add delay to force positive dts if (s->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) { write_metadata(s, ts); s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED; } switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: avio_w8(pb, FLV_TAG_TYPE_VIDEO); flags = ff_codec_get_tag(flv_video_codec_ids, par->codec_id); flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, par); assert(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); break; case AVMEDIA_TYPE_DATA: avio_w8(pb, FLV_TAG_TYPE_META); break; default: return AVERROR(EINVAL); } if (par->codec_id == AV_CODEC_ID_H264) /* check if extradata looks like MP4 */ if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0) return -1; /* check Speex packet duration */ if (par->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160) av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); if (sc->last_ts < ts) sc->last_ts = ts; avio_wb24(pb, size + flags_size); avio_wb24(pb, ts); avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_ avio_wb24(pb, flv->reserved); if (par->codec_type == AVMEDIA_TYPE_DATA) { int data_size; int64_t metadata_size_pos = avio_tell(pb); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onTextData"); avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); avio_wb32(pb, 2); put_amf_string(pb, "type"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "Text"); put_amf_string(pb, "text"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, pkt->data); put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); /* write total size of tag */ data_size = avio_tell(pb) - metadata_size_pos; avio_seek(pb, metadata_size_pos - 10, SEEK_SET); avio_wb24(pb, data_size); avio_seek(pb, data_size + 10 - 3, SEEK_CUR); avio_wb32(pb, data_size + 11); } else { avio_w8(pb,flags); if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A) { if (par->extradata_size) avio_w8(pb, par->extradata[0]); else avio_w8(pb, ((FFALIGN(par->width, 16) - par->width) << 4) | (FFALIGN(par->height, 16) - par->height)); } else if (par->codec_id == AV_CODEC_ID_AAC) avio_w8(pb, 1); // AAC raw else if (par->codec_id == AV_CODEC_ID_H264) { avio_w8(pb, 1); // AVC NALU avio_wb24(pb, pkt->pts - pkt->dts); } avio_write(pb, data ? data : pkt->data, size); avio_wb32(pb, size + flags_size + 11); // previous tag size flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); } av_free(data); return pb->error; }
static int flv_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; AVCodecContext *audio_enc = NULL, *video_enc = NULL; int i, metadata_count = 0; double framerate = 0.0; int64_t metadata_size_pos, data_size, metadata_count_pos; AVDictionaryEntry *tag = NULL; for(i=0; i<s->nb_streams; i++){ AVCodecContext *enc = s->streams[i]->codec; FLVStreamContext *sc; if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { if (s->streams[i]->r_frame_rate.den && s->streams[i]->r_frame_rate.num) { framerate = av_q2d(s->streams[i]->r_frame_rate); } else { framerate = 1/av_q2d(s->streams[i]->codec->time_base); } video_enc = enc; if(enc->codec_tag == 0) { av_log(enc, AV_LOG_ERROR, "video codec not compatible with flv\n"); return -1; } } else { audio_enc = enc; if(get_audio_flags(enc)<0) return -1; } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ sc = av_mallocz(sizeof(FLVStreamContext)); if (!sc) return AVERROR(ENOMEM); s->streams[i]->priv_data = sc; sc->last_ts = -1; } flv->delay = AV_NOPTS_VALUE; avio_write(pb, "FLV", 3); avio_w8(pb,1); avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!audio_enc + FLV_HEADER_FLAG_HASVIDEO * !!video_enc); avio_wb32(pb,9); avio_wb32(pb,0); for(i=0; i<s->nb_streams; i++){ if(s->streams[i]->codec->codec_tag == 5){ avio_w8(pb,8); // message type avio_wb24(pb,0); // include flags avio_wb24(pb,0); // time stamp avio_wb32(pb,0); // reserved avio_wb32(pb,11); // size flv->reserved=5; } } /* write meta_tag */ avio_w8(pb, 18); // tag type META metadata_size_pos= avio_tell(pb); avio_wb24(pb, 0); // size of data part (sum of all parts below) avio_wb24(pb, 0); // time stamp avio_wb32(pb, 0); // reserved /* now data of data_size size */ /* first event name as a string */ avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onMetaData"); // 12 bytes /* mixed array (hash) with size and string/type/data tuples */ avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); metadata_count_pos = avio_tell(pb); metadata_count = 5*!!video_enc + 5*!!audio_enc + 2; // +2 for duration and file size avio_wb32(pb, metadata_count); put_amf_string(pb, "duration"); flv->duration_offset= avio_tell(pb); put_amf_double(pb, s->duration / AV_TIME_BASE); // fill in the guessed duration, it'll be corrected later if incorrect if(video_enc){ put_amf_string(pb, "width"); put_amf_double(pb, video_enc->width); put_amf_string(pb, "height"); put_amf_double(pb, video_enc->height); put_amf_string(pb, "videodatarate"); put_amf_double(pb, video_enc->bit_rate / 1024.0); put_amf_string(pb, "framerate"); put_amf_double(pb, framerate); put_amf_string(pb, "videocodecid"); put_amf_double(pb, video_enc->codec_tag); } if(audio_enc){ put_amf_string(pb, "audiodatarate"); put_amf_double(pb, audio_enc->bit_rate / 1024.0); put_amf_string(pb, "audiosamplerate"); put_amf_double(pb, audio_enc->sample_rate); put_amf_string(pb, "audiosamplesize"); put_amf_double(pb, audio_enc->codec_id == CODEC_ID_PCM_U8 ? 8 : 16); put_amf_string(pb, "stereo"); put_amf_bool(pb, audio_enc->channels == 2); put_amf_string(pb, "audiocodecid"); put_amf_double(pb, audio_enc->codec_tag); } while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { put_amf_string(pb, tag->key); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, tag->value); metadata_count++; } put_amf_string(pb, "filesize"); flv->filesize_offset= avio_tell(pb); put_amf_double(pb, 0); // delayed write put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); /* write total size of tag */ data_size= avio_tell(pb) - metadata_size_pos - 10; avio_seek(pb, metadata_count_pos, SEEK_SET); avio_wb32(pb, metadata_count); avio_seek(pb, metadata_size_pos, SEEK_SET); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); for (i = 0; i < s->nb_streams; i++) { AVCodecContext *enc = s->streams[i]->codec; if (enc->codec_id == CODEC_ID_AAC || enc->codec_id == CODEC_ID_H264) { int64_t pos; avio_w8(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO ? FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); avio_wb24(pb, 0); // size patched later avio_wb24(pb, 0); // ts avio_w8(pb, 0); // ts ext avio_wb24(pb, 0); // streamid pos = avio_tell(pb); if (enc->codec_id == CODEC_ID_AAC) { avio_w8(pb, get_audio_flags(enc)); avio_w8(pb, 0); // AAC sequence header avio_write(pb, enc->extradata, enc->extradata_size); } else { avio_w8(pb, enc->codec_tag | FLV_FRAME_KEY); // flags avio_w8(pb, 0); // AVC sequence header avio_wb24(pb, 0); // composition time ff_isom_write_avcc(pb, enc->extradata, enc->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); // previous tag size } } return 0; }
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; FLVContext *flv = s->priv_data; FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size = pkt->size; uint8_t *data = NULL; int flags = -1, flags_size, ret; int64_t cur_offset = avio_tell(pb); if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A || par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC) flags_size = 2; else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) flags_size = 5; else flags_size = 1; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { int side_size = 0; uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { av_free(par->extradata); par->extradata = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!par->extradata) { par->extradata_size = 0; return AVERROR(ENOMEM); } memcpy(par->extradata, side, side_size); par->extradata_size = side_size; flv_write_codec_header(s, par); } } if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; if (pkt->dts < -flv->delay) { av_log(s, AV_LOG_WARNING, "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } ts = pkt->dts; if (s->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) { write_metadata(s, ts); s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED; } avio_write_marker(pb, av_rescale(ts, AV_TIME_BASE, 1000), pkt->flags & AV_PKT_FLAG_KEY && (flv->video_par ? par->codec_type == AVMEDIA_TYPE_VIDEO : 1) ? AVIO_DATA_MARKER_SYNC_POINT : AVIO_DATA_MARKER_BOUNDARY_POINT); switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: avio_w8(pb, FLV_TAG_TYPE_VIDEO); flags = ff_codec_get_tag(flv_video_codec_ids, par->codec_id); flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, par); av_assert0(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); break; case AVMEDIA_TYPE_SUBTITLE: case AVMEDIA_TYPE_DATA: avio_w8(pb, FLV_TAG_TYPE_META); break; default: return AVERROR(EINVAL); } if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { /* check if extradata looks like mp4 formatted */ if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) return ret; } else if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { if (!s->streams[pkt->stream_index]->nb_frames) { av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " "use the audio bitstream filter 'aac_adtstoasc' to fix it " "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); return AVERROR_INVALIDDATA; } av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); } /* check Speex packet duration */ if (par->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160) av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); if (sc->last_ts < ts) sc->last_ts = ts; if (size + flags_size >= 1<<24) { av_log(s, AV_LOG_ERROR, "Too large packet with size %u >= %u\n", size + flags_size, 1<<24); return AVERROR(EINVAL); } avio_wb24(pb, size + flags_size); avio_wb24(pb, ts & 0xFFFFFF); avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_ avio_wb24(pb, flv->reserved); if (par->codec_type == AVMEDIA_TYPE_DATA || par->codec_type == AVMEDIA_TYPE_SUBTITLE ) { int data_size; int64_t metadata_size_pos = avio_tell(pb); if (par->codec_id == AV_CODEC_ID_TEXT) { // legacy FFmpeg magic? avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onTextData"); avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); avio_wb32(pb, 2); put_amf_string(pb, "type"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "Text"); put_amf_string(pb, "text"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, pkt->data); put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); } else { // just pass the metadata through avio_write(pb, data ? data : pkt->data, size); } /* write total size of tag */ data_size = avio_tell(pb) - metadata_size_pos; avio_seek(pb, metadata_size_pos - 10, SEEK_SET); avio_wb24(pb, data_size); avio_seek(pb, data_size + 10 - 3, SEEK_CUR); avio_wb32(pb, data_size + 11); } else { av_assert1(flags>=0); avio_w8(pb,flags); if (par->codec_id == AV_CODEC_ID_VP6) avio_w8(pb,0); if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A) { if (par->extradata_size) avio_w8(pb, par->extradata[0]); else avio_w8(pb, ((FFALIGN(par->width, 16) - par->width) << 4) | (FFALIGN(par->height, 16) - par->height)); } else if (par->codec_id == AV_CODEC_ID_AAC) avio_w8(pb, 1); // AAC raw else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { avio_w8(pb, 1); // AVC NALU avio_wb24(pb, pkt->pts - pkt->dts); } avio_write(pb, data ? data : pkt->data, size); avio_wb32(pb, size + flags_size + 11); // previous tag size flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); } if (flv->flags & FLV_ADD_KEYFRAME_INDEX) { switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: flv->videosize += (avio_tell(pb) - cur_offset); flv->lasttimestamp = flv->acurframeindex / flv->framerate; if (pkt->flags & AV_PKT_FLAG_KEY) { double ts = flv->acurframeindex / flv->framerate; int64_t pos = cur_offset; flv->lastkeyframetimestamp = flv->acurframeindex / flv->framerate; flv->lastkeyframelocation = pos; flv_append_keyframe_info(s, flv, ts, pos); } flv->acurframeindex++; break; case AVMEDIA_TYPE_AUDIO: flv->audiosize += (avio_tell(pb) - cur_offset); break; default: av_log(s, AV_LOG_WARNING, "par->codec_type is type = [%d]\n", par->codec_type); break; } } av_free(data); return pb->error; }
static int flv_write_header(AVFormatContext *s) { int i; AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; FLVStreamContext *sc; switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->streams[i]->avg_frame_rate.den && s->streams[i]->avg_frame_rate.num) { flv->framerate = av_q2d(s->streams[i]->avg_frame_rate); } if (flv->video_par) { av_log(s, AV_LOG_ERROR, "at most one video stream is supported in flv\n"); return AVERROR(EINVAL); } flv->video_par = par; if (!ff_codec_get_tag(flv_video_codec_ids, par->codec_id)) return unsupported_codec(s, "Video", par->codec_id); if (par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_H263) { int error = s->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL; av_log(s, error ? AV_LOG_ERROR : AV_LOG_WARNING, "Codec %s is not supported in the official FLV specification,\n", avcodec_get_name(par->codec_id)); if (error) { av_log(s, AV_LOG_ERROR, "use vstrict=-1 / -strict -1 to use it anyway.\n"); return AVERROR(EINVAL); } } else if (par->codec_id == AV_CODEC_ID_VP6) { av_log(s, AV_LOG_WARNING, "Muxing VP6 in flv will produce flipped video on playback.\n"); } break; case AVMEDIA_TYPE_AUDIO: if (flv->audio_par) { av_log(s, AV_LOG_ERROR, "at most one audio stream is supported in flv\n"); return AVERROR(EINVAL); } flv->audio_par = par; if (get_audio_flags(s, par) < 0) return unsupported_codec(s, "Audio", par->codec_id); if (par->codec_id == AV_CODEC_ID_PCM_S16BE) av_log(s, AV_LOG_WARNING, "16-bit big-endian audio in flv is valid but most likely unplayable (hardware dependent); use s16le\n"); break; case AVMEDIA_TYPE_DATA: if (par->codec_id != AV_CODEC_ID_TEXT && par->codec_id != AV_CODEC_ID_NONE) return unsupported_codec(s, "Data", par->codec_id); flv->data_par = par; break; case AVMEDIA_TYPE_SUBTITLE: if (par->codec_id != AV_CODEC_ID_TEXT) { av_log(s, AV_LOG_ERROR, "Subtitle codec '%s' for stream %d is not compatible with FLV\n", avcodec_get_name(par->codec_id), i); return AVERROR_INVALIDDATA; } flv->data_par = par; break; default: av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with FLV\n", av_get_media_type_string(par->codec_type), i); return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ sc = av_mallocz(sizeof(FLVStreamContext)); if (!sc) return AVERROR(ENOMEM); s->streams[i]->priv_data = sc; sc->last_ts = -1; } flv->delay = AV_NOPTS_VALUE; avio_write(pb, "FLV", 3); avio_w8(pb, 1); avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!flv->audio_par + FLV_HEADER_FLAG_HASVIDEO * !!flv->video_par); avio_wb32(pb, 9); avio_wb32(pb, 0); for (i = 0; i < s->nb_streams; i++) if (s->streams[i]->codecpar->codec_tag == 5) { avio_w8(pb, 8); // message type avio_wb24(pb, 0); // include flags avio_wb24(pb, 0); // time stamp avio_wb32(pb, 0); // reserved avio_wb32(pb, 11); // size flv->reserved = 5; } if (flv->flags & FLV_NO_METADATA) { pb->seekable = 0; } else { write_metadata(s, 0); } for (i = 0; i < s->nb_streams; i++) { flv_write_codec_header(s, s->streams[i]->codecpar); } flv->datastart_offset = avio_tell(pb); return 0; }
static int shift_data(AVFormatContext *s) { int ret = 0; int n = 0; int64_t metadata_size = 0; FLVContext *flv = s->priv_data; int64_t pos, pos_end = avio_tell(s->pb); uint8_t *buf, *read_buf[2]; int read_buf_id = 0; int read_size[2]; AVIOContext *read_pb; metadata_size = flv->filepositions_count * 9 * 2 + 10; /* filepositions and times value */ metadata_size += 2 + 13; /* filepositions String */ metadata_size += 2 + 5; /* times String */ metadata_size += 3; /* Object end */ flv->keyframe_index_size = metadata_size; if (metadata_size < 0) return metadata_size; buf = av_malloc_array(metadata_size, 2); if (!buf) { return AVERROR(ENOMEM); } read_buf[0] = buf; read_buf[1] = buf + metadata_size; avio_seek(s->pb, flv->metadata_size_pos, SEEK_SET); avio_wb24(s->pb, flv->metadata_totalsize + metadata_size); avio_seek(s->pb, flv->metadata_totalsize_pos, SEEK_SET); avio_wb32(s->pb, flv->metadata_totalsize + 11 + metadata_size); avio_seek(s->pb, pos_end, SEEK_SET); /* Shift the data: the AVIO context of the output can only be used for * writing, so we re-open the same output, but for reading. It also avoids * a read/seek/write/seek back and forth. */ avio_flush(s->pb); ret = s->io_open(s, &read_pb, s->filename, AVIO_FLAG_READ, NULL); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to re-open %s output file for " "the second pass (add_keyframe_index)\n", s->filename); goto end; } /* mark the end of the shift to up to the last data we wrote, and get ready * for writing */ pos_end = avio_tell(s->pb); avio_seek(s->pb, flv->keyframes_info_offset + metadata_size, SEEK_SET); /* start reading at where the keyframe index information will be placed */ avio_seek(read_pb, flv->keyframes_info_offset, SEEK_SET); pos = avio_tell(read_pb); /* shift data by chunk of at most keyframe *filepositions* and *times* size */ read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], metadata_size); \ read_buf_id ^= 1; do { read_size[read_buf_id] = avio_read(read_pb, read_buf[read_buf_id], metadata_size); \ read_buf_id ^= 1; n = read_size[read_buf_id]; if (n < 0) break; avio_write(s->pb, read_buf[read_buf_id], n); pos += n; } while (pos <= pos_end); ff_format_io_close(s, &read_pb); end: av_free(buf); return ret; }
static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par) { int64_t data_size; AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { int64_t pos; avio_w8(pb, par->codec_type == AVMEDIA_TYPE_VIDEO ? FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); avio_wb24(pb, 0); // size patched later avio_wb24(pb, 0); // ts avio_w8(pb, 0); // ts ext avio_wb24(pb, 0); // streamid pos = avio_tell(pb); if (par->codec_id == AV_CODEC_ID_AAC) { avio_w8(pb, get_audio_flags(s, par)); avio_w8(pb, 0); // AAC sequence header if (!par->extradata_size && (flv->flags & FLV_AAC_SEQ_HEADER_DETECT)) { PutBitContext pbc; int samplerate_index; int channels = flv->audio_par->channels - (flv->audio_par->channels == 8 ? 1 : 0); uint8_t data[2]; for (samplerate_index = 0; samplerate_index < 16; samplerate_index++) if (flv->audio_par->sample_rate == mpeg4audio_sample_rates[samplerate_index]) break; init_put_bits(&pbc, data, sizeof(data)); put_bits(&pbc, 5, flv->audio_par->profile + 1); //profile put_bits(&pbc, 4, samplerate_index); //sample rate index put_bits(&pbc, 4, channels); put_bits(&pbc, 1, 0); //frame length - 1024 samples put_bits(&pbc, 1, 0); //does not depend on core coder put_bits(&pbc, 1, 0); //is not extension flush_put_bits(&pbc); avio_w8(pb, data[0]); avio_w8(pb, data[1]); av_log(s, AV_LOG_WARNING, "AAC sequence header: %02x %02x.\n", data[0], data[1]); } avio_write(pb, par->extradata, par->extradata_size); } else { avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags avio_w8(pb, 0); // AVC sequence header avio_wb24(pb, 0); // composition time ff_isom_write_avcc(pb, par->extradata, par->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); // previous tag size } }
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; AVCodecContext *enc = s->streams[pkt->stream_index]->codec; FLVContext *flv = s->priv_data; FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size = pkt->size; uint8_t *data = NULL; int flags, flags_size; // av_log(s, AV_LOG_DEBUG, "type:%d pts: %"PRId64" size:%d\n", // enc->codec_type, timestamp, size); if (enc->codec_id == CODEC_ID_VP6 || enc->codec_id == CODEC_ID_VP6F || enc->codec_id == CODEC_ID_VP6A || enc->codec_id == CODEC_ID_AAC) flags_size = 2; else if (enc->codec_id == CODEC_ID_H264 || enc->codec_id == CODEC_ID_MPEG4) flags_size = 5; else flags_size = 1; switch (enc->codec_type) { case AVMEDIA_TYPE_VIDEO: avio_w8(pb, FLV_TAG_TYPE_VIDEO); flags = enc->codec_tag; if (flags == 0) { av_log(s, AV_LOG_ERROR, "video codec %s not compatible with flv\n", avcodec_get_name(enc->codec_id)); return -1; } flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, enc); av_assert0(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); break; case AVMEDIA_TYPE_DATA: avio_w8(pb, FLV_TAG_TYPE_META); break; default: return AVERROR(EINVAL); } if (enc->codec_id == CODEC_ID_H264 || enc->codec_id == CODEC_ID_MPEG4) { /* check if extradata looks like mp4 formated */ if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0) return -1; } else if (enc->codec_id == CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { av_log(s, AV_LOG_ERROR, "malformated aac bitstream, use -absf aac_adtstoasc\n"); return -1; } if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; if (pkt->dts < -flv->delay) { av_log(s, AV_LOG_WARNING, "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } ts = pkt->dts + flv->delay; // add delay to force positive dts /* check Speex packet duration */ if (enc->codec_id == CODEC_ID_SPEEX && ts - sc->last_ts > 160) av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); if (sc->last_ts < ts) sc->last_ts = ts; avio_wb24(pb, size + flags_size); avio_wb24(pb, ts); avio_w8(pb, (ts >> 24) & 0x7F); // timestamps are 32 bits _signed_ avio_wb24(pb, flv->reserved); if (enc->codec_type == AVMEDIA_TYPE_DATA) { int data_size; int metadata_size_pos = avio_tell(pb); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onTextData"); avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); avio_wb32(pb, 2); put_amf_string(pb, "type"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "Text"); put_amf_string(pb, "text"); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, pkt->data); put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); /* write total size of tag */ data_size = avio_tell(pb) - metadata_size_pos; avio_seek(pb, metadata_size_pos - 10, SEEK_SET); avio_wb24(pb, data_size); avio_seek(pb, data_size + 10 - 3, SEEK_CUR); avio_wb32(pb, data_size + 11); } else { avio_w8(pb,flags); if (enc->codec_id == CODEC_ID_VP6) avio_w8(pb,0); if (enc->codec_id == CODEC_ID_VP6F || enc->codec_id == CODEC_ID_VP6A) avio_w8(pb, enc->extradata_size ? enc->extradata[0] : 0); else if (enc->codec_id == CODEC_ID_AAC) avio_w8(pb,1); // AAC raw else if (enc->codec_id == CODEC_ID_H264 || enc->codec_id == CODEC_ID_MPEG4) { avio_w8(pb,1); // AVC NALU avio_wb24(pb,pkt->pts - pkt->dts); } avio_write(pb, data ? data : pkt->data, size); avio_wb32(pb, size + flags_size + 11); // previous tag size flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); } avio_flush(pb); av_free(data); return pb->error; }
static int flv_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; AVCodecContext *audio_enc = NULL, *video_enc = NULL, *data_enc = NULL; int i, metadata_count = 0; double framerate = 0.0; int64_t metadata_size_pos, data_size, metadata_count_pos; AVDictionaryEntry *tag = NULL; for (i = 0; i < s->nb_streams; i++) { AVCodecContext *enc = s->streams[i]->codec; FLVStreamContext *sc; switch (enc->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->streams[i]->avg_frame_rate.den && s->streams[i]->avg_frame_rate.num) { framerate = av_q2d(s->streams[i]->avg_frame_rate); } if (video_enc) { av_log(s, AV_LOG_ERROR, "at most one video stream is supported in flv\n"); return AVERROR(EINVAL); } video_enc = enc; if (enc->codec_tag == 0) { av_log(s, AV_LOG_ERROR, "Video codec '%s' for stream %d is not compatible with FLV\n", avcodec_get_name(enc->codec_id), i); return AVERROR(EINVAL); } if (enc->codec_id == AV_CODEC_ID_MPEG4 || enc->codec_id == AV_CODEC_ID_H263) { int error = enc->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL; av_log(s, error ? AV_LOG_ERROR : AV_LOG_WARNING, "Codec %s is not supported in the official FLV specification,\n", avcodec_get_name(enc->codec_id)); if (error) { av_log(s, AV_LOG_ERROR, "use vstrict=-1 / -strict -1 to use it anyway.\n"); return AVERROR(EINVAL); } } break; case AVMEDIA_TYPE_AUDIO: if (audio_enc) { av_log(s, AV_LOG_ERROR, "at most one audio stream is supported in flv\n"); return AVERROR(EINVAL); } audio_enc = enc; if (get_audio_flags(s, enc) < 0) return AVERROR_INVALIDDATA; if (enc->codec_id == AV_CODEC_ID_PCM_S16BE) av_log(s, AV_LOG_WARNING, "16-bit big-endian audio in flv is valid but most likely unplayable (hardware dependent); use s16le\n"); break; case AVMEDIA_TYPE_DATA: if (enc->codec_id != AV_CODEC_ID_TEXT) { av_log(s, AV_LOG_ERROR, "Data codec '%s' for stream %d is not compatible with FLV\n", avcodec_get_name(enc->codec_id), i); return AVERROR_INVALIDDATA; } data_enc = enc; break; default: av_log(s, AV_LOG_ERROR, "Codec type '%s' for stream %d is not compatible with FLV\n", av_get_media_type_string(enc->codec_type), i); return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ sc = av_mallocz(sizeof(FLVStreamContext)); if (!sc) return AVERROR(ENOMEM); s->streams[i]->priv_data = sc; sc->last_ts = -1; } flv->delay = AV_NOPTS_VALUE; avio_write(pb, "FLV", 3); avio_w8(pb, 1); avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!audio_enc + FLV_HEADER_FLAG_HASVIDEO * !!video_enc); avio_wb32(pb, 9); avio_wb32(pb, 0); for (i = 0; i < s->nb_streams; i++) if (s->streams[i]->codec->codec_tag == 5) { avio_w8(pb, 8); // message type avio_wb24(pb, 0); // include flags avio_wb24(pb, 0); // time stamp avio_wb32(pb, 0); // reserved avio_wb32(pb, 11); // size flv->reserved = 5; } /* write meta_tag */ avio_w8(pb, 18); // tag type META metadata_size_pos = avio_tell(pb); avio_wb24(pb, 0); // size of data part (sum of all parts below) avio_wb24(pb, 0); // timestamp avio_wb32(pb, 0); // reserved /* now data of data_size size */ /* first event name as a string */ avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onMetaData"); // 12 bytes /* mixed array (hash) with size and string/type/data tuples */ avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); metadata_count_pos = avio_tell(pb); metadata_count = 4 * !!video_enc + 5 * !!audio_enc + 1 * !!data_enc + 2; // +2 for duration and file size avio_wb32(pb, metadata_count); put_amf_string(pb, "duration"); flv->duration_offset= avio_tell(pb); // fill in the guessed duration, it'll be corrected later if incorrect put_amf_double(pb, s->duration / AV_TIME_BASE); if (video_enc) { put_amf_string(pb, "width"); put_amf_double(pb, video_enc->width); put_amf_string(pb, "height"); put_amf_double(pb, video_enc->height); put_amf_string(pb, "videodatarate"); put_amf_double(pb, video_enc->bit_rate / 1024.0); if (framerate != 0.0) { put_amf_string(pb, "framerate"); put_amf_double(pb, framerate); metadata_count++; } put_amf_string(pb, "videocodecid"); put_amf_double(pb, video_enc->codec_tag); } if (audio_enc) { put_amf_string(pb, "audiodatarate"); put_amf_double(pb, audio_enc->bit_rate / 1024.0); put_amf_string(pb, "audiosamplerate"); put_amf_double(pb, audio_enc->sample_rate); put_amf_string(pb, "audiosamplesize"); put_amf_double(pb, audio_enc->codec_id == AV_CODEC_ID_PCM_U8 ? 8 : 16); put_amf_string(pb, "stereo"); put_amf_bool(pb, audio_enc->channels == 2); put_amf_string(pb, "audiocodecid"); put_amf_double(pb, audio_enc->codec_tag); } if (data_enc) { put_amf_string(pb, "datastream"); put_amf_double(pb, 0.0); } while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { if( !strcmp(tag->key, "width") ||!strcmp(tag->key, "height") ||!strcmp(tag->key, "videodatarate") ||!strcmp(tag->key, "framerate") ||!strcmp(tag->key, "videocodecid") ||!strcmp(tag->key, "audiodatarate") ||!strcmp(tag->key, "audiosamplerate") ||!strcmp(tag->key, "audiosamplesize") ||!strcmp(tag->key, "stereo") ||!strcmp(tag->key, "audiocodecid") ||!strcmp(tag->key, "duration") ||!strcmp(tag->key, "onMetaData") ){ av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key); continue; } put_amf_string(pb, tag->key); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, tag->value); metadata_count++; } put_amf_string(pb, "filesize"); flv->filesize_offset = avio_tell(pb); put_amf_double(pb, 0); // delayed write put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); /* write total size of tag */ data_size = avio_tell(pb) - metadata_size_pos - 10; avio_seek(pb, metadata_count_pos, SEEK_SET); avio_wb32(pb, metadata_count); avio_seek(pb, metadata_size_pos, SEEK_SET); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); for (i = 0; i < s->nb_streams; i++) { AVCodecContext *enc = s->streams[i]->codec; if (enc->codec_id == AV_CODEC_ID_AAC || enc->codec_id == AV_CODEC_ID_H264 || enc->codec_id == AV_CODEC_ID_MPEG4) { int64_t pos; avio_w8(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO ? FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); avio_wb24(pb, 0); // size patched later avio_wb24(pb, 0); // ts avio_w8(pb, 0); // ts ext avio_wb24(pb, 0); // streamid pos = avio_tell(pb); if (enc->codec_id == AV_CODEC_ID_AAC) { avio_w8(pb, get_audio_flags(s, enc)); avio_w8(pb, 0); // AAC sequence header avio_write(pb, enc->extradata, enc->extradata_size); } else { avio_w8(pb, enc->codec_tag | FLV_FRAME_KEY); // flags avio_w8(pb, 0); // AVC sequence header avio_wb24(pb, 0); // composition time ff_isom_write_avcc(pb, enc->extradata, enc->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); // previous tag size } } return 0; }
static int flv_write_header(AVFormatContext *s) { int i; AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; int64_t data_size; for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; FLVStreamContext *sc; switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->streams[i]->avg_frame_rate.den && s->streams[i]->avg_frame_rate.num) { flv->framerate = av_q2d(s->streams[i]->avg_frame_rate); } if (flv->video_par) { av_log(s, AV_LOG_ERROR, "at most one video stream is supported in flv\n"); return AVERROR(EINVAL); } flv->video_par = par; if (!ff_codec_get_tag(flv_video_codec_ids, par->codec_id)) return unsupported_codec(s, "Video", par->codec_id); break; case AVMEDIA_TYPE_AUDIO: if (flv->audio_par) { av_log(s, AV_LOG_ERROR, "at most one audio stream is supported in flv\n"); return AVERROR(EINVAL); } flv->audio_par = par; if (get_audio_flags(s, par) < 0) return unsupported_codec(s, "Audio", par->codec_id); break; case AVMEDIA_TYPE_DATA: if (par->codec_id != AV_CODEC_ID_TEXT) return unsupported_codec(s, "Data", par->codec_id); flv->data_par = par; break; default: av_log(s, AV_LOG_ERROR, "codec not compatible with flv\n"); return -1; } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ sc = av_mallocz(sizeof(FLVStreamContext)); if (!sc) return AVERROR(ENOMEM); s->streams[i]->priv_data = sc; sc->last_ts = -1; } flv->delay = AV_NOPTS_VALUE; avio_write(pb, "FLV", 3); avio_w8(pb, 1); avio_w8(pb, FLV_HEADER_FLAG_HASAUDIO * !!flv->audio_par + FLV_HEADER_FLAG_HASVIDEO * !!flv->video_par); avio_wb32(pb, 9); avio_wb32(pb, 0); for (i = 0; i < s->nb_streams; i++) if (s->streams[i]->codecpar->codec_tag == 5) { avio_w8(pb, 8); // message type avio_wb24(pb, 0); // include flags avio_wb24(pb, 0); // time stamp avio_wb32(pb, 0); // reserved avio_wb32(pb, 11); // size flv->reserved = 5; } write_metadata(s, 0); for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264) { int64_t pos; avio_w8(pb, par->codec_type == AVMEDIA_TYPE_VIDEO ? FLV_TAG_TYPE_VIDEO : FLV_TAG_TYPE_AUDIO); avio_wb24(pb, 0); // size patched later avio_wb24(pb, 0); // ts avio_w8(pb, 0); // ts ext avio_wb24(pb, 0); // streamid pos = avio_tell(pb); if (par->codec_id == AV_CODEC_ID_AAC) { avio_w8(pb, get_audio_flags(s, par)); avio_w8(pb, 0); // AAC sequence header avio_write(pb, par->extradata, par->extradata_size); } else { avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags avio_w8(pb, 0); // AVC sequence header avio_wb24(pb, 0); // composition time ff_isom_write_avcc(pb, par->extradata, par->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); // previous tag size } } return 0; }
static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; AVCodecContext *enc = s->streams[pkt->stream_index]->codec; FLVContext *flv = s->priv_data; FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size= pkt->size; uint8_t *data= NULL; int flags, flags_size; // av_log(s, AV_LOG_DEBUG, "type:%d pts: %"PRId64" size:%d\n", enc->codec_type, timestamp, size); if(enc->codec_id == CODEC_ID_VP6 || enc->codec_id == CODEC_ID_VP6F || enc->codec_id == CODEC_ID_AAC) flags_size= 2; else if(enc->codec_id == CODEC_ID_H264) flags_size= 5; else flags_size= 1; if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { avio_w8(pb, FLV_TAG_TYPE_VIDEO); flags = enc->codec_tag; if(flags == 0) { av_log(enc, AV_LOG_ERROR, "video codec %X not compatible with flv\n",enc->codec_id); return -1; } flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; } else { assert(enc->codec_type == AVMEDIA_TYPE_AUDIO); flags = get_audio_flags(enc); assert(size); avio_w8(pb, FLV_TAG_TYPE_AUDIO); } if (enc->codec_id == CODEC_ID_H264) { /* check if extradata looks like MP4 */ if (enc->extradata_size > 0 && *(uint8_t*)enc->extradata != 1) { if (ff_avc_parse_nal_units_buf(pkt->data, &data, &size) < 0) return -1; } } if (flv->delay == AV_NOPTS_VALUE) flv->delay = -pkt->dts; if (pkt->dts < -flv->delay) { av_log(s, AV_LOG_WARNING, "Packets are not in the proper order with " "respect to DTS\n"); return AVERROR(EINVAL); } ts = pkt->dts + flv->delay; // add delay to force positive dts /* check Speex packet duration */ if (enc->codec_id == CODEC_ID_SPEEX && ts - sc->last_ts > 160) { av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); } if (sc->last_ts < ts) sc->last_ts = ts; avio_wb24(pb,size + flags_size); avio_wb24(pb,ts); avio_w8(pb,(ts >> 24) & 0x7F); // timestamps are 32bits _signed_ avio_wb24(pb,flv->reserved); avio_w8(pb,flags); if (enc->codec_id == CODEC_ID_VP6) avio_w8(pb,0); if (enc->codec_id == CODEC_ID_VP6F) avio_w8(pb, enc->extradata_size ? enc->extradata[0] : 0); else if (enc->codec_id == CODEC_ID_AAC) avio_w8(pb,1); // AAC raw else if (enc->codec_id == CODEC_ID_H264) { avio_w8(pb,1); // AVC NALU avio_wb24(pb,pkt->pts - pkt->dts); } avio_write(pb, data ? data : pkt->data, size); avio_wb32(pb,size+flags_size+11); // previous tag size flv->duration = FFMAX(flv->duration, pkt->pts + flv->delay + pkt->duration); avio_flush(pb); av_free(data); return pb->error; }
static void write_metadata(AVFormatContext *s, unsigned int ts) { AVIOContext *pb = s->pb; FLVContext *flv = s->priv_data; int metadata_count = 0; int64_t metadata_size_pos, data_size, metadata_count_pos; AVDictionaryEntry *tag = NULL; /* write meta_tag */ avio_w8(pb, 18); // tag type META metadata_size_pos = avio_tell(pb); avio_wb24(pb, 0); // size of data part (sum of all parts below) avio_wb24(pb, ts); // timestamp avio_wb32(pb, 0); // reserved /* now data of data_size size */ /* first event name as a string */ avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, "onMetaData"); // 12 bytes /* mixed array (hash) with size and string/type/data tuples */ avio_w8(pb, AMF_DATA_TYPE_MIXEDARRAY); metadata_count_pos = avio_tell(pb); metadata_count = 4 * !!flv->video_enc + 5 * !!flv->audio_enc + 1 * !!flv->data_enc + 2; // +2 for duration and file size avio_wb32(pb, metadata_count); put_amf_string(pb, "duration"); flv->duration_offset = avio_tell(pb); // fill in the guessed duration, it'll be corrected later if incorrect put_amf_double(pb, s->duration / AV_TIME_BASE); if (flv->video_enc) { put_amf_string(pb, "width"); put_amf_double(pb, flv->video_enc->width); put_amf_string(pb, "height"); put_amf_double(pb, flv->video_enc->height); put_amf_string(pb, "videodatarate"); put_amf_double(pb, flv->video_enc->bit_rate / 1024.0); if (flv->framerate != 0.0) { put_amf_string(pb, "framerate"); put_amf_double(pb, flv->framerate); metadata_count++; } put_amf_string(pb, "videocodecid"); put_amf_double(pb, flv->video_enc->codec_tag); } if (flv->audio_enc) { put_amf_string(pb, "audiodatarate"); put_amf_double(pb, flv->audio_enc->bit_rate / 1024.0); put_amf_string(pb, "audiosamplerate"); put_amf_double(pb, flv->audio_enc->sample_rate); put_amf_string(pb, "audiosamplesize"); put_amf_double(pb, flv->audio_enc->codec_id == AV_CODEC_ID_PCM_U8 ? 8 : 16); put_amf_string(pb, "stereo"); put_amf_bool(pb, flv->audio_enc->channels == 2); put_amf_string(pb, "audiocodecid"); put_amf_double(pb, flv->audio_enc->codec_tag); } if (flv->data_enc) { put_amf_string(pb, "datastream"); put_amf_double(pb, 0.0); } while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { if( !strcmp(tag->key, "width") ||!strcmp(tag->key, "height") ||!strcmp(tag->key, "videodatarate") ||!strcmp(tag->key, "framerate") ||!strcmp(tag->key, "videocodecid") ||!strcmp(tag->key, "audiodatarate") ||!strcmp(tag->key, "audiosamplerate") ||!strcmp(tag->key, "audiosamplesize") ||!strcmp(tag->key, "stereo") ||!strcmp(tag->key, "audiocodecid") ||!strcmp(tag->key, "duration") ||!strcmp(tag->key, "onMetaData") ||!strcmp(tag->key, "datasize") ||!strcmp(tag->key, "lasttimestamp") ||!strcmp(tag->key, "totalframes") ||!strcmp(tag->key, "hasAudio") ||!strcmp(tag->key, "hasVideo") ||!strcmp(tag->key, "hasCuePoints") ||!strcmp(tag->key, "hasMetadata") ||!strcmp(tag->key, "hasKeyframes") ){ av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key); continue; } put_amf_string(pb, tag->key); avio_w8(pb, AMF_DATA_TYPE_STRING); put_amf_string(pb, tag->value); metadata_count++; } put_amf_string(pb, "filesize"); flv->filesize_offset = avio_tell(pb); put_amf_double(pb, 0); // delayed write put_amf_string(pb, ""); avio_w8(pb, AMF_END_OF_OBJECT); /* write total size of tag */ data_size = avio_tell(pb) - metadata_size_pos - 10; avio_seek(pb, metadata_count_pos, SEEK_SET); avio_wb32(pb, metadata_count); avio_seek(pb, metadata_size_pos, SEEK_SET); avio_wb24(pb, data_size); avio_skip(pb, data_size + 10 - 3); avio_wb32(pb, data_size + 11); }