Академический Документы
Профессиональный Документы
Культура Документы
based on www.dranger.com/ffmpeg
Agenda
tutorial 5 - synching video tutorial 6 - synching audio tutorial 7 - seeking tutorial 8 - software scaling Homework 2 - design issues
Tutorial 5
This tutorial start demonstrating sync we will need to explain the term DTS - decoding time stamp PTS - presentation time stamp The PTS says when to display the frame the DTS says when to decode the frame
DTS vs PTS
Streams sometime have several passes on the source stream. on the second stream they encode information that include future frame information on current frame. Frames that relay on future frame are called B-Frame and can only be decoded once future frames been decoded In that case there may be frame to be decoded first but displayed later....
Clock
We need to sync for something. Audio (runs normal video catches up) Video (runs normal audio catches up) External clock This tutorial sync video to the audio which is the less recommended version
UNITS
(this is not well covered by the tutorial) Timestamp is calculated in the following way - for each frame we are given its time in the stream. (i.e. when to display it!) in integral units. if we have the 47 frame in 25 fps stream the stream should be played at 47/25s after start... av_q2d is a conversion from rational (a/b a,b integers) to real (double) number.
setting PTS
double pts; for(;;) { if(packet_queue_get(&is->videoq, packet, 1) < 0) { // means we quit getting packets break; } pts = 0; // Decode video frame len1 = avcodec_decode_video(is->video_st->codec, pFrame, &frameFinished, packet->data, packet->size); if(packet->dts != AV_NOPTS_VALUE) { pts = packet->dts; } else { pts = 0; } pts *= av_q2d(is->video_st->time_base);
Getting PTS
Getting the PTS is possible if we give FFMPEG our frame allocation and deallocation functions They should match these prototypes
int get_buffer(struct AVCodecContext *c, AVFrame *pic); void release_buffer(struct AVCodecContext *c, AVFrame *pic);
Explaining
av_malloc is a wrapper to malloc av_free is a wrapper to free that also set the pointer to null (no dangling pointers!) we set our handlers using
codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer=our_release_buffer;
Final code
for(;;) { if(packet_queue_get(&is->videoq, packet, 1) < 0) { // means we quit getting packets break; } pts = 0; // Save global pts to be stored in pFrame in first call global_video_pkt_pts = packet->pts; // Decode video frame len1 = avcodec_decode_video(is->video_st->codec, pFrame, &frameFinished, packet->data, packet->size); if(packet->dts == AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; } else if(packet->dts != AV_NOPTS_VALUE) { pts = packet->dts; } else { pts = 0; } pts *= av_q2d(is->video_st->time_base);
Syncing to audio...
Adding the following typedef struct VideoState { ... double video_clock;
Synchronised video
double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) { double frame_delay; if(pts != 0) { /* if we have pts, set video clock to it */ is->video_clock = pts; } else { /* if we aren't given a pts, set it to the clock */ pts = is->video_clock; } /* update the video clock */ frame_delay = av_q2d(is->video_st->codec->time_base); /* if we are repeating a frame, adjust clock accordingly */ frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); is->video_clock += frame_delay; return pts;
Explaining
Tutorial 6 improves the clock by adding a master clock. Possible - this is the most important chapter of the tutorial (with respect to implementing HW2)
In theory - application specific. if we watch rock concert the last thing we want is to stretch or delete audio in order to sync with video... In practice - External or Video works best!
int synchronize_audio(VideoState *is, short *samples, int samples_size, double pts) { int n; double ref_clock; n = 2 * is->audio_st->codec->channels; if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) { double diff, avg_diff; int wanted_size, min_size, max_size, nb_samples; ref_clock = get_master_clock(is); diff = get_audio_clock(is) - ref_clock; if(diff < AV_NOSYNC_THRESHOLD) { // accumulate the diffs is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { is->audio_diff_avg_count++; } else { avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); /* Shrinking/expanding buffer code.... */ } } else { /* difference is TOO big; reset diff stuff */ is->audio_diff_avg_count = 0; is->audio_diff_cum = 0; } } return samples_size; }
Explaining
This function stretches or shrinks (drop or duplicate frames) the audio when ever it is not used as the main clock!
Tutorial 7 - seeking
This tutorial teaches us how to do seeking (moving the video back and forward by 10 or 60 seconds intervals) It is important to us for 3 reasons We need to learn how to get key strokes to change the video Flushing buffers - in some designs we may want to do buffer flashing as well. Seeking - Seeking is important for some bonuses.
Getting keystrokes
The main loop is getting events (such as keystrokes) we can later switch for actual keystrokes
switch(event.type) { case SDL_KEYDOWN: switch(event.key.keysym.sym) { case SDLK_LEFT: incr = -10.0; goto do_seek; do_seek: if(global_video_state) { pos = get_master_clock(global_video_state); pos += incr; stream_seek(global_video_state, (int64_t)(pos * AV_TIME_BASE), incr); } break;
Flushing buffers
Codecs keep track on current frame. Each new frame usually carries with it changes (delta) from current frame. not a new image. Sending delta from new stream to old will not work. (new stream contains new delta!) We need to flush when we seek. Also we may need to flush on channel change.
Flush packets
We add flush packet to the queues to inform the av_codec they need to flush its buffer Logic Flush queue Put flush packet in queue Get flush packet from queue call avcodec_flush_buffers
if(is->audioStream >= 0) { packet_queue_flush(&is->audioq); packet_queue_put(&is->audioq, &flush_pkt); } if(is->videoStream >= 0) { packet_queue_flush(&is->videoq); packet_queue_put(&is->videoq, &flush_pkt); }
When we need to flush, flush the queue and then put the flush packet inthe empty queue
int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; if(pkt != &flush_pkt && av_dup_packet(pkt) < 0) { return -1; }
FLUSH
Actual seeking
do_seek: if(global_video_state) { pos = get_master_clock(global_video_state); pos += incr; stream_seek(global_video_state, (int64_t)(pos * AV_TIME_BASE), incr); }
This code is calling for function that request seeking from decode thread.
void stream_seek(VideoState *is, int64_t pos, int rel) { if(!is->seek_req) { is->seek_pos = pos; is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0; is->seek_req = 1; } }
EXPLAINING AV FUNCTIONS
The av_rescale_q - change units from seconds to frames required for stream The av_seek_frame - handles actual seeking.
Tutorial 8
This tutorial is of least importance to us it teaches how to use the swscale library instead of img_convert we have been doing this all the time Its only important to us to scale for bonus implementations (PIP etc.)
Ex2
The goal of this exercise is to read several files while playing one. It can be stressful on your PC. If you download high quality stream (compressed with h264 etc.) increase in the virtual box the number of virtual cpus to two and also increase the memory given to the VM. Preferably use low bit rate 240*360 streams. (240p) You may use streams with only images for video.
Architecture
There can be several architectures to do it... Read several files and decode several files while eventually on playback deciding what to play. Create a final audio queue to be played (replicating the audio arch) Create several readers but keep only one decoder (assume all streams have a same codec)
Bonus
The bonuses are all checker discretion. The more complete your app is the better the bonus. Bonuses for effects - Picture in picture/side by side (sws_scale) Bonus for control - grab frame (PPM-P6), Fast Forward, Fast Backward etc. Windows (Same old R&R... compile/download+CMakelist.txt)