int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt, int track_index, int sample) { MOVMuxContext *mov = s->priv_data; MOVTrack *trk = &mov->tracks[track_index]; AVFormatContext *rtp_ctx = trk->rtp_ctx; uint8_t *buf = NULL; int size; AVIOContext *hintbuf = NULL; AVPacket hint_pkt; int ret = 0, count; if (!rtp_ctx) return AVERROR(ENOENT); if (!rtp_ctx->pb) return AVERROR(ENOMEM); sample_queue_push(&trk->sample_queue, pkt, sample); /* Feed the packet to the RTP muxer */ ff_write_chained(rtp_ctx, 0, pkt, s); /* Fetch the output from the RTP muxer, open a new output buffer * for next time. */ size = url_close_dyn_buf(rtp_ctx->pb, &buf); if ((ret = url_open_dyn_packet_buf(&rtp_ctx->pb, RTP_MAX_PACKET_SIZE)) < 0) goto done; if (size <= 0) goto done; /* Open a buffer for writing the hint */ if ((ret = url_open_dyn_buf(&hintbuf)) < 0) goto done; av_init_packet(&hint_pkt); count = write_hint_packets(hintbuf, buf, size, trk, &hint_pkt.dts); av_freep(&buf); /* Write the hint data into the hint track */ hint_pkt.size = size = url_close_dyn_buf(hintbuf, &buf); hint_pkt.data = buf; hint_pkt.pts = hint_pkt.dts; hint_pkt.stream_index = track_index; if (pkt->flags & AV_PKT_FLAG_KEY) hint_pkt.flags |= AV_PKT_FLAG_KEY; if (count > 0) ff_mov_write_packet(s, &hint_pkt); done: av_free(buf); sample_queue_retain(&trk->sample_queue); return ret; }
int ff_avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size) { ByteIOContext *pb; int ret = url_open_dyn_buf(&pb); if(ret < 0) return ret; ff_avc_parse_nal_units(pb, buf_in, *size); av_freep(buf); *size = url_close_dyn_buf(pb, buf); return 0; }
static void put_str16(ByteIOContext *s, const char *tag) { int len; uint8_t *pb; ByteIOContext *dyn_buf; if (url_open_dyn_buf(&dyn_buf) < 0) return; avio_put_str16le(dyn_buf, tag); len = url_close_dyn_buf(dyn_buf, &pb); put_le16(s, len); put_buffer(s, pb, len); av_freep(&pb); }
void rtp_send_punch_packets(URLContext* rtp_handle) { AVIOContext *pb; uint8_t *buf; int len; /* Send a small RTP packet */ if (url_open_dyn_buf(&pb) < 0) return; avio_w8(pb, (RTP_VERSION << 6)); avio_w8(pb, 0); /* Payload type */ avio_wb16(pb, 0); /* Seq */ avio_wb32(pb, 0); /* Timestamp */ avio_wb32(pb, 0); /* SSRC */ avio_flush(pb); len = url_close_dyn_buf(pb, &buf); if ((len > 0) && buf) url_write(rtp_handle, buf, len); av_free(buf); /* Send a minimal RTCP RR */ if (url_open_dyn_buf(&pb) < 0) return; avio_w8(pb, (RTP_VERSION << 6)); avio_w8(pb, RTCP_RR); /* receiver report */ avio_wb16(pb, 1); /* length in words - 1 */ avio_wb32(pb, 0); /* our own SSRC */ avio_flush(pb); len = url_close_dyn_buf(pb, &buf); if ((len > 0) && buf) url_write(rtp_handle, buf, len); av_free(buf); }
int ff_avc_parse_nal_units(const uint8_t *buf_in, uint8_t **buf, int *size) { ByteIOContext pb; const uint8_t *p = buf_in; const uint8_t *end = p + *size; const uint8_t *nal_start, *nal_end; int ret = url_open_dyn_buf(&pb); if(ret < 0) return ret; nal_start = ff_avc_find_startcode(p, end); while (nal_start < end) { while(!*(nal_start++)); nal_end = ff_avc_find_startcode(nal_start, end); put_be32(&pb, nal_end - nal_start); put_buffer(&pb, nal_start, nal_end - nal_start); nal_start = nal_end; } av_freep(buf); *size = url_close_dyn_buf(&pb, buf); return 0; }
static int ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags) { OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data; ByteIOContext *pb; int64_t crc_offset; int ret, size; uint8_t *buf; ret = url_open_dyn_buf(&pb); if (ret < 0) return ret; init_checksum(pb, ff_crc04C11DB7_update, 0); put_tag(pb, "OggS"); put_byte(pb, 0); put_byte(pb, page->flags | extra_flags); put_le64(pb, page->granule); put_le32(pb, oggstream->serial_num); put_le32(pb, oggstream->page_counter++); crc_offset = url_ftell(pb); put_le32(pb, 0); // crc put_byte(pb, page->segments_count); put_buffer(pb, page->segments, page->segments_count); put_buffer(pb, page->data, page->size); ogg_update_checksum(s, pb, crc_offset); put_flush_packet(pb); size = url_close_dyn_buf(pb, &buf); if (size < 0) return size; put_buffer(s->pb, buf, size); put_flush_packet(s->pb); av_free(buf); oggstream->page_count--; return 0; }
int rtp_check_and_send_back_rr(RTPDemuxContext *s, int count) { AVIOContext *pb; uint8_t *buf; int len; int rtcp_bytes; RTPStatistics *stats= &s->statistics; uint32_t lost; uint32_t extended_max; uint32_t expected_interval; uint32_t received_interval; uint32_t lost_interval; uint32_t expected; uint32_t fraction; uint64_t ntp_time= s->last_rtcp_ntp_time; // TODO: Get local ntp time? if (!s->rtp_ctx || (count < 1)) return -1; /* TODO: I think this is way too often; RFC 1889 has algorithm for this */ /* XXX: mpeg pts hardcoded. RTCP send every 0.5 seconds */ s->octet_count += count; rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) / RTCP_TX_RATIO_DEN; rtcp_bytes /= 50; // mmu_man: that's enough for me... VLC sends much less btw !? if (rtcp_bytes < 28) return -1; s->last_octet_count = s->octet_count; if (url_open_dyn_buf(&pb) < 0) return -1; // Receiver Report avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */ avio_w8(pb, RTCP_RR); avio_wb16(pb, 7); /* length in words - 1 */ // our own SSRC: we use the server's SSRC + 1 to avoid conflicts avio_wb32(pb, s->ssrc + 1); avio_wb32(pb, s->ssrc); // server SSRC // some placeholders we should really fill... // RFC 1889/p64 extended_max= stats->cycles + stats->max_seq; expected= extended_max - stats->base_seq + 1; lost= expected - stats->received; lost= FFMIN(lost, 0xffffff); // clamp it since it's only 24 bits... expected_interval= expected - stats->expected_prior; stats->expected_prior= expected; received_interval= stats->received - stats->received_prior; stats->received_prior= stats->received; lost_interval= expected_interval - received_interval; if (expected_interval==0 || lost_interval<=0) fraction= 0; else fraction = (lost_interval<<8)/expected_interval; fraction= (fraction<<24) | lost; avio_wb32(pb, fraction); /* 8 bits of fraction, 24 bits of total packets lost */ avio_wb32(pb, extended_max); /* max sequence received */ avio_wb32(pb, stats->jitter>>4); /* jitter */ if(s->last_rtcp_ntp_time==AV_NOPTS_VALUE) { avio_wb32(pb, 0); /* last SR timestamp */ avio_wb32(pb, 0); /* delay since last SR */ } else { uint32_t middle_32_bits= s->last_rtcp_ntp_time>>16; // this is valid, right? do we need to handle 64 bit values special? uint32_t delay_since_last= ntp_time - s->last_rtcp_ntp_time; avio_wb32(pb, middle_32_bits); /* last SR timestamp */ avio_wb32(pb, delay_since_last); /* delay since last SR */ } // CNAME avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */ avio_w8(pb, RTCP_SDES); len = strlen(s->hostname); avio_wb16(pb, (6 + len + 3) / 4); /* length in words - 1 */ avio_wb32(pb, s->ssrc); avio_w8(pb, 0x01); avio_w8(pb, len); avio_write(pb, s->hostname, len); // padding for (len = (6 + len) % 4; len % 4; len++) { avio_w8(pb, 0); } avio_flush(pb); len = url_close_dyn_buf(pb, &buf); if ((len > 0) && buf) { int result; av_dlog(s->ic, "sending %d bytes of RR\n", len); result= url_write(s->rtp_ctx, buf, len); av_dlog(s->ic, "result from url_write: %d\n", result); av_free(buf); } return 0; }
/* write the header (used two times if non streamed) */ static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data_chunk_size) { ASFContext *asf = s->priv_data; ByteIOContext *pb = s->pb; AVMetadataTag *tags[5]; int header_size, n, extra_size, extra_size2, wav_extra_size, file_time; int has_title; int metadata_count; AVCodecContext *enc; int64_t header_offset, cur_pos, hpos; int bit_rate; int64_t duration; ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL); tags[0] = av_metadata_get(s->metadata, "title" , NULL, 0); tags[1] = av_metadata_get(s->metadata, "author" , NULL, 0); tags[2] = av_metadata_get(s->metadata, "copyright", NULL, 0); tags[3] = av_metadata_get(s->metadata, "comment" , NULL, 0); tags[4] = av_metadata_get(s->metadata, "rating" , NULL, 0); duration = asf->duration + PREROLL_TIME * 10000; has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4]; metadata_count = s->metadata ? s->metadata->count : 0; bit_rate = 0; for(n=0;n<s->nb_streams;n++) { enc = s->streams[n]->codec; av_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */ bit_rate += enc->bit_rate; } if (asf->is_streamed) { put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */ } put_guid(pb, &ff_asf_header); put_le64(pb, -1); /* header length, will be patched after */ put_le32(pb, 3 + has_title + !!metadata_count + s->nb_streams); /* number of chunks in header */ put_byte(pb, 1); /* ??? */ put_byte(pb, 2); /* ??? */ /* file header */ header_offset = url_ftell(pb); hpos = put_header(pb, &ff_asf_file_header); put_guid(pb, &ff_asf_my_guid); put_le64(pb, file_size); file_time = 0; put_le64(pb, unix_to_file_time(file_time)); put_le64(pb, asf->nb_packets); /* number of packets */ put_le64(pb, duration); /* end time stamp (in 100ns units) */ put_le64(pb, asf->duration); /* duration (in 100ns units) */ put_le64(pb, PREROLL_TIME); /* start time stamp */ put_le32(pb, (asf->is_streamed || url_is_streamed(pb)) ? 3 : 2); /* ??? */ put_le32(pb, s->packet_size); /* packet size */ put_le32(pb, s->packet_size); /* packet size */ put_le32(pb, bit_rate); /* Nominal data rate in bps */ end_header(pb, hpos); /* unknown headers */ hpos = put_header(pb, &ff_asf_head1_guid); put_guid(pb, &ff_asf_head2_guid); put_le32(pb, 6); put_le16(pb, 0); end_header(pb, hpos); /* title and other infos */ if (has_title) { int len; uint8_t *buf; ByteIOContext *dyn_buf; if (url_open_dyn_buf(&dyn_buf) < 0) return AVERROR(ENOMEM); hpos = put_header(pb, &ff_asf_comment_header); for (n = 0; n < FF_ARRAY_ELEMS(tags); n++) { len = tags[n] ? avio_put_str16le(dyn_buf, tags[n]->value) : 0; put_le16(pb, len); } len = url_close_dyn_buf(dyn_buf, &buf); put_buffer(pb, buf, len); av_freep(&buf); end_header(pb, hpos); } if (metadata_count) { AVMetadataTag *tag = NULL; hpos = put_header(pb, &ff_asf_extended_content_header); put_le16(pb, metadata_count); while ((tag = av_metadata_get(s->metadata, "", tag, AV_METADATA_IGNORE_SUFFIX))) { put_str16(pb, tag->key); put_le16(pb, 0); put_str16(pb, tag->value); } end_header(pb, hpos); } /* stream headers */ for(n=0;n<s->nb_streams;n++) { int64_t es_pos; // ASFStream *stream = &asf->streams[n]; enc = s->streams[n]->codec; asf->streams[n].num = n + 1; asf->streams[n].seq = 0; switch(enc->codec_type) { case AVMEDIA_TYPE_AUDIO: wav_extra_size = 0; extra_size = 18 + wav_extra_size; extra_size2 = 8; break; default: case AVMEDIA_TYPE_VIDEO: wav_extra_size = enc->extradata_size; extra_size = 0x33 + wav_extra_size; extra_size2 = 0; break; } hpos = put_header(pb, &ff_asf_stream_header); if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { put_guid(pb, &ff_asf_audio_stream); put_guid(pb, &ff_asf_audio_conceal_spread); } else { put_guid(pb, &ff_asf_video_stream); put_guid(pb, &ff_asf_video_conceal_none); } put_le64(pb, 0); /* ??? */ es_pos = url_ftell(pb); put_le32(pb, extra_size); /* wav header len */ put_le32(pb, extra_size2); /* additional data len */ put_le16(pb, n + 1); /* stream number */ put_le32(pb, 0); /* ??? */ if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { /* WAVEFORMATEX header */ int wavsize = ff_put_wav_header(pb, enc); if ((enc->codec_id != CODEC_ID_MP3) && (enc->codec_id != CODEC_ID_MP2) && (enc->codec_id != CODEC_ID_ADPCM_IMA_WAV) && (enc->extradata_size==0)) { wavsize += 2; put_le16(pb, 0); } if (wavsize < 0) return -1; if (wavsize != extra_size) { cur_pos = url_ftell(pb); url_fseek(pb, es_pos, SEEK_SET); put_le32(pb, wavsize); /* wav header len */ url_fseek(pb, cur_pos, SEEK_SET); } /* ERROR Correction */ put_byte(pb, 0x01); if(enc->codec_id == CODEC_ID_ADPCM_G726 || !enc->block_align){ put_le16(pb, 0x0190); put_le16(pb, 0x0190); }else{ put_le16(pb, enc->block_align); put_le16(pb, enc->block_align); } put_le16(pb, 0x01); put_byte(pb, 0x00); } else { put_le32(pb, enc->width); put_le32(pb, enc->height); put_byte(pb, 2); /* ??? */ put_le16(pb, 40 + enc->extradata_size); /* size */ /* BITMAPINFOHEADER header */ ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1); } end_header(pb, hpos); } /* media comments */ hpos = put_header(pb, &ff_asf_codec_comment_header); put_guid(pb, &ff_asf_codec_comment1_header); put_le32(pb, s->nb_streams); for(n=0;n<s->nb_streams;n++) { AVCodec *p; const char *desc; int len; uint8_t *buf; ByteIOContext *dyn_buf; enc = s->streams[n]->codec; p = avcodec_find_encoder(enc->codec_id); if(enc->codec_type == AVMEDIA_TYPE_AUDIO) put_le16(pb, 2); else if(enc->codec_type == AVMEDIA_TYPE_VIDEO) put_le16(pb, 1); else put_le16(pb, -1); if(enc->codec_id == CODEC_ID_WMAV2) desc = "Windows Media Audio V8"; else desc = p ? p->name : enc->codec_name; if ( url_open_dyn_buf(&dyn_buf) < 0) return AVERROR(ENOMEM); avio_put_str16le(dyn_buf, desc); len = url_close_dyn_buf(dyn_buf, &buf); put_le16(pb, len / 2); // "number of characters" = length in bytes / 2 put_buffer(pb, buf, len); av_freep(&buf); put_le16(pb, 0); /* no parameters */ /* id */ if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { put_le16(pb, 2); put_le16(pb, enc->codec_tag); } else { put_le16(pb, 4); put_le32(pb, enc->codec_tag); } if(!enc->codec_tag) return -1; } end_header(pb, hpos); /* patch the header size fields */ cur_pos = url_ftell(pb); header_size = cur_pos - header_offset; if (asf->is_streamed) { header_size += 8 + 30 + 50; url_fseek(pb, header_offset - 10 - 30, SEEK_SET); put_le16(pb, header_size); url_fseek(pb, header_offset - 2 - 30, SEEK_SET); put_le16(pb, header_size); header_size -= 8 + 30 + 50; } header_size += 24 + 6; url_fseek(pb, header_offset - 14, SEEK_SET); put_le64(pb, header_size); url_fseek(pb, cur_pos, SEEK_SET); /* movie chunk, followed by packets of packet_size */ asf->data_offset = cur_pos; put_guid(pb, &ff_asf_data_header); put_le64(pb, data_chunk_size); put_guid(pb, &ff_asf_my_guid); put_le64(pb, asf->nb_packets); /* nb packets */ put_byte(pb, 1); /* ??? */ put_byte(pb, 1); /* ??? */ return 0; }
/** * @return 0 when a packet was written into /p pkt, and no more data is left; * 1 when a packet was written into /p pkt, and more packets might be left; * <0 when not enough data was provided to return a full packet, or on error. */ static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf, AVStream *st, AVPacket *pkt, uint32_t *timestamp, const uint8_t *buf, int len, int flags) { ByteIOContext *pb = &asf->pb; int res, mflags, len_off; RTSPState *rt = s->priv_data; if (!rt->asf_ctx) return -1; if (len > 0) { int off, out_len = 0; if (len < 4) return -1; av_freep(&asf->buf); init_put_byte(pb, buf, len, 0, NULL, NULL, NULL, NULL); while (url_ftell(pb) + 4 < len) { int start_off = url_ftell(pb); mflags = get_byte(pb); if (mflags & 0x80) flags |= RTP_FLAG_KEY; len_off = get_be24(pb); if (mflags & 0x20) /**< relative timestamp */ url_fskip(pb, 4); if (mflags & 0x10) /**< has duration */ url_fskip(pb, 4); if (mflags & 0x8) /**< has location ID */ url_fskip(pb, 4); off = url_ftell(pb); if (!(mflags & 0x40)) { /** * If 0x40 is not set, the len_off field specifies an offset * of this packet's payload data in the complete (reassembled) * ASF packet. This is used to spread one ASF packet over * multiple RTP packets. */ if (asf->pktbuf && len_off != url_ftell(asf->pktbuf)) { uint8_t *p; url_close_dyn_buf(asf->pktbuf, &p); asf->pktbuf = NULL; av_free(p); } if (!len_off && !asf->pktbuf && (res = url_open_dyn_buf(&asf->pktbuf)) < 0) return res; if (!asf->pktbuf) return AVERROR(EIO); put_buffer(asf->pktbuf, buf + off, len - off); url_fskip(pb, len - off); if (!(flags & RTP_FLAG_MARKER)) return -1; out_len = url_close_dyn_buf(asf->pktbuf, &asf->buf); asf->pktbuf = NULL; } else { /** * If 0x40 is set, the len_off field specifies the length of * the next ASF packet that can be read from this payload * data alone. This is commonly the same as the payload size, * but could be less in case of packet splitting (i.e. * multiple ASF packets in one RTP packet). */ int cur_len = start_off + len_off - off; int prev_len = out_len; out_len += cur_len; asf->buf = av_realloc(asf->buf, out_len); memcpy(asf->buf + prev_len, buf + off, FFMIN(cur_len, len - off)); url_fskip(pb, cur_len); } } init_packetizer(pb, asf->buf, out_len); pb->pos += rt->asf_pb_pos; pb->eof_reached = 0; rt->asf_ctx->pb = pb; } for (;;) { int i; res = av_read_packet(rt->asf_ctx, pkt); rt->asf_pb_pos = url_ftell(pb); if (res != 0) break; for (i = 0; i < s->nb_streams; i++) { if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) { pkt->stream_index = i; return 1; // FIXME: return 0 if last packet } } av_free_packet(pkt); } return res == 1 ? -1 : res; }
/** return 0 on packet, <0 on partial packet or error... */ static int svq3_parse_packet (AVFormatContext *s, PayloadContext *sv, AVStream *st, AVPacket *pkt, uint32_t *timestamp, const uint8_t *buf, int len, int flags) { int config_packet, start_packet, end_packet; if (len < 2) return AVERROR_INVALIDDATA; config_packet = buf[0] & 0x40; start_packet = buf[0] & 0x20; end_packet = buf[0] & 0x10; buf += 2; // ignore buf[1] len -= 2; if (config_packet) { av_freep(&st->codec->extradata); st->codec->extradata_size = 0; if (len < 2 || !(st->codec->extradata = av_malloc(len + 8 + FF_INPUT_BUFFER_PADDING_SIZE))) return AVERROR_INVALIDDATA; st->codec->extradata_size = len + 8; memcpy(st->codec->extradata, "SEQH", 4); AV_WB32(st->codec->extradata + 4, len); memcpy(st->codec->extradata + 8, buf, len); /* We set codec_id to CODEC_ID_NONE initially to * delay decoder initialization since extradata is * carried within the RTP stream, not SDP. Here, * by setting codec_id to CODEC_ID_SVQ3, we are signalling * to the decoder that it is OK to initialize. */ st->codec->codec_id = CODEC_ID_SVQ3; return AVERROR(EAGAIN); } if (start_packet) { int res; if (sv->pktbuf) { uint8_t *tmp; url_close_dyn_buf(sv->pktbuf, &tmp); av_free(tmp); } if ((res = url_open_dyn_buf(&sv->pktbuf)) < 0) return res; sv->timestamp = *timestamp; } if (!sv->pktbuf) return AVERROR_INVALIDDATA; put_buffer(sv->pktbuf, buf, len); if (end_packet) { av_init_packet(pkt); pkt->stream_index = st->index; *timestamp = sv->timestamp; pkt->size = url_close_dyn_buf(sv->pktbuf, &pkt->data); pkt->destruct = av_destruct_packet; sv->pktbuf = NULL; return 0; } return AVERROR(EAGAIN); }
static int vp8_handle_packet(AVFormatContext *ctx, PayloadContext *vp8, AVStream *st, AVPacket *pkt, uint32_t *timestamp, const uint8_t *buf, int len, int flags) { int start_packet, end_packet, has_au, ret = AVERROR(EAGAIN); if (!buf) { // only called when vp8_handle_packet returns 1 if (!vp8->data) { av_log(ctx, AV_LOG_ERROR, "Invalid VP8 data passed\n"); return AVERROR_INVALIDDATA; } prepare_packet(pkt, vp8, st->index); *timestamp = vp8->timestamp; return 0; } start_packet = *buf & 1; end_packet = flags & RTP_FLAG_MARKER; has_au = *buf & 2; buf++; len--; if (start_packet) { int res; uint32_t ts = *timestamp; if (vp8->data) { // missing end marker; return old frame anyway. untested prepare_packet(pkt, vp8, st->index); *timestamp = vp8->timestamp; // reset timestamp from old frame // if current frame fits into one rtp packet, need to hold // that for the next av_get_packet call ret = end_packet ? 1 : 0; } if ((res = url_open_dyn_buf(&vp8->data)) < 0) return res; vp8->is_keyframe = *buf & 1; vp8->timestamp = ts; } if (!vp8->data || vp8->timestamp != *timestamp && ret == AVERROR(EAGAIN)) { av_log(ctx, AV_LOG_WARNING, "Received no start marker; dropping frame\n"); return AVERROR(EAGAIN); } // cycle through VP8AU headers if needed // not tested with actual VP8AUs while (len) { int au_len = len; if (has_au && len > 2) { au_len = AV_RB16(buf); buf += 2; len -= 2; if (buf + au_len > buf + len) { av_log(ctx, AV_LOG_ERROR, "Invalid VP8AU length\n"); return AVERROR_INVALIDDATA; } } put_buffer(vp8->data, buf, au_len); buf += au_len; len -= au_len; } if (ret != AVERROR(EAGAIN)) // did we miss a end marker? return ret; if (end_packet) { prepare_packet(pkt, vp8, st->index); return 0; } return AVERROR(EAGAIN); }