see build_ffmpeg on linux for how to build ffmpeg easily
1 2 3 4 5
| export LD_LIBRARY_PATH=~/code//ffmpeg/install/lib:. cd ~/code//ffmpeg/install/bin
./ffplay -i /e/RES/Videos/h2645/sample-An.mp4 -threads 1 -filter_threads 1
|
threads
- ffplay: main thread, sleeping
- SDLTimer: some Timer stuffs
- PulseHotPlug: pulse audio thread
- read_thread: demuxer thread
- video_decoder: decoder thread
video decode process
read_thread:
1 2
| ret = av_read_frame(ic, pkt); packet_queue_put(&is->videoq, pkt);
|
video_decoder:
1 2 3 4 5 6 7 8 9 10 11
|
get_video_frame(){ decoder_decode_frame(){ if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0) return -1; avcodec_send_packet(d->avctx, d->pkt); avcodec_receive_frame(d->avctx, frame); } queue_picture(is, frame, pts, duration, fd ? fd->pkt_pos : -1, is->viddec.pkt_serial); }
|
buffer control
read_thread: control the buffer size of stream data
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #define MAX_QUEUE_SIZE (15 * 1024 * 1024)
if (infinite_buffer<1 && (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE || (stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq) && stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq) && stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq)))) { SDL_LockMutex(wait_mutex); SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10); SDL_UnlockMutex(wait_mutex); continue; }
|
video_decoder: keep pulling data queued from read_thread, and stop when queued pictures size exceeds
note the value of VIDEO_PICTURE_QUEUE_SIZE
1 2 3 4 5 6 7 8 9
| #define VIDEO_PICTURE_QUEUE_SIZE 3 #define SUBPICTURE_QUEUE_SIZE 16 #define SAMPLE_QUEUE_SIZE 9 video_thread::queue_picture(){ frame_queue_peek_writable(){ while (f->size >= f->max_size && !f->pktq->abort_request) SDL_CondWait(f->cond, f->mutex); } }
|
seek
the key is the concept serial
, of both PacketQueue::serial
, MyAVPacketList::serial
in PacketQueue list, and Decoder::pkt_serial
.
ffplay use serial
to syn its state.
like when seek happens, the PacketQueue
change it serial, then the demuxed packets, finally the Decoder
object.
for the seek:
- :arrow_left: Left-Arrow Keydown
- ffpaly: in SDL event_loop: set {seek_req, seek_flag}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| event_loop(){ case SDLK_LEFT: incr = seek_interval ? -seek_interval : -10.0; goto do_seek; ... do_seek: stream_seek(cur_stream, pos, incr, 1); }
static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int by_bytes) { if (!is->seek_req) { is->seek_pos = pos; is->seek_rel = rel; is->seek_flags &= ~AVSEEK_FLAG_BYTE; if (by_bytes) is->seek_flags |= AVSEEK_FLAG_BYTE; is->seek_req = 1; SDL_CondSignal(is->continue_read_thread); } }
|
- read_thread: seek demuxer and flush packet_buffer
note serial is incre by 1 in packet_queue_flush
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static int read_thread(void *arg){ for(;;){ if (is->seek_req) { ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags); packet_queue_flush(&is->videoq); } } } static void packet_queue_flush(PacketQueue *q) { MyAVPacketList pkt1;
SDL_LockMutex(q->mutex); while (av_fifo_read(q->pkt_list, &pkt1, 1) >= 0) av_packet_free(&pkt1.pkt); q->nb_packets = 0; q->size = 0; q->duration = 0; q->serial++; SDL_UnlockMutex(q->mutex); }
static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt) { ... pkt1.serial = q->serial; ... }
|
- video_decoder: avcodec_flush_buffers when serials unmatch
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { do{ int old_serial = d->pkt_serial; if (packet_queue_get(d->queue, d->pkt, 1, &d->pkt_serial) < 0) return -1; if (old_serial != d->pkt_serial) { avcodec_flush_buffers(d->avctx); d->finished = 0; d->next_pts = d->start_pts; d->next_pts_tb = d->start_pts_tb; } } while (1); }
|