[Parte 2/2] Guia para FFmpeg e SDL ou Como gravar um player de vídeo em menos de 1000 linhas


Publicamos o restante da tradução para o russo do manual, que está um pouco desatualizado, mas não perdeu sua relevância, pois este tutorial ajuda a aprofundar a “cozinha” da criação de aplicativos de vídeo usando as bibliotecas FFmpeg e SDL.

E, embora tenhamos tentado, dificuldades de tradução são inevitáveis ​​em um texto tão volumoso . Relatar bugs (de preferência em mensagens privadas) - juntos faremos melhor.

Índice


EDISON Software - desenvolvimento web
EDISON.

, .

, , Axxon Next SureView Immix.

! ;-)

6:


tutorial06.c
// tutorial05.c
// A pedagogical video player that really works!
//
// Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, 
// and a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
// With updates from https://github.com/chelyaev/ffmpeg-tutorial
// Updates tested on:
// LAVC 54.59.100, LAVF 54.29.104, LSWS 2.1.101, SDL 1.2.15
// on GCC 4.7.2 in Debian February 2015
// Use
//
// gcc -o tutorial05 tutorial05.c -lavformat -lavcodec -lswscale -lz -lm `sdl-config --cflags --libs`
// to build (assuming libavformat and libavcodec are correctly installed, 
// and assuming you have sdl-config. Please refer to SDL docs for your installation.)
//
// Run using
// tutorial04 myvideofile.mpg
//
// to play the video stream on your screen.

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <SDL.h>
#include <SDL_thread.h>

#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif

#include <stdio.h>
#include <assert.h>
#include <math.h>

// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif

#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000

#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)

#define AV_SYNC_THRESHOLD 0.01
#define AV_NOSYNC_THRESHOLD 10.0

#define SAMPLE_CORRECTION_PERCENT_MAX 10
#define AUDIO_DIFF_AVG_NB 20

#define FF_REFRESH_EVENT (SDL_USEREVENT)
#define FF_QUIT_EVENT (SDL_USEREVENT + 1)

#define VIDEO_PICTURE_QUEUE_SIZE 1

#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER

typedef struct PacketQueue {
  AVPacketList *first_pkt, *last_pkt;
  int nb_packets;
  int size;
  SDL_mutex *mutex;
  SDL_cond *cond;
} PacketQueue;


typedef struct VideoPicture {
  SDL_Overlay *bmp;
  int width, height; /* source height & width */
  int allocated;
  double pts;
} VideoPicture;

typedef struct VideoState {

  AVFormatContext *pFormatCtx;
  int             videoStream, audioStream;

  int             av_sync_type;
  double          external_clock; /* external clock base */
  int64_t         external_clock_time;

  double          audio_clock;
  AVStream        *audio_st;
  AVCodecContext  *audio_ctx;
  PacketQueue     audioq;
  uint8_t         audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
  unsigned int    audio_buf_size;
  unsigned int    audio_buf_index;
  AVFrame         audio_frame;
  AVPacket        audio_pkt;
  uint8_t         *audio_pkt_data;
  int             audio_pkt_size;
  int             audio_hw_buf_size;
  double          audio_diff_cum; /* used for AV difference average computation */
  double          audio_diff_avg_coef;
  double          audio_diff_threshold;
  int             audio_diff_avg_count;
  double          frame_timer;
  double          frame_last_pts;
  double          frame_last_delay;
  double          video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame
  double          video_current_pts; ///<current displayed pts (different from video_clock if frame fifos are used)
  int64_t         video_current_pts_time;  ///<time (av_gettime) at which we updated video_current_pts - used to have running video pts
  AVStream        *video_st;
  AVCodecContext  *video_ctx;
  PacketQueue     videoq;
  struct SwsContext *sws_ctx;

  VideoPicture    pictq[VIDEO_PICTURE_QUEUE_SIZE];
  int             pictq_size, pictq_rindex, pictq_windex;
  SDL_mutex       *pictq_mutex;
  SDL_cond        *pictq_cond;
  
  SDL_Thread      *parse_tid;
  SDL_Thread      *video_tid;

  char            filename[1024];
  int             quit;
} VideoState;

enum {
  AV_SYNC_AUDIO_MASTER,
  AV_SYNC_VIDEO_MASTER,
  AV_SYNC_EXTERNAL_MASTER,
};

SDL_Surface     *screen;
SDL_mutex       *screen_mutex;

/* Since we only have one decoding thread, the Big Struct
   can be global in case we need it. */
VideoState *global_video_state;

void packet_queue_init(PacketQueue *q) {
  memset(q, 0, sizeof(PacketQueue));
  q->mutex = SDL_CreateMutex();
  q->cond = SDL_CreateCond();
}
int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

  AVPacketList *pkt1;
  if(av_dup_packet(pkt) < 0) {
    return -1;
  }
  pkt1 = av_malloc(sizeof(AVPacketList));
  if (!pkt1)
    return -1;
  pkt1->pkt = *pkt;
  pkt1->next = NULL;
  
  SDL_LockMutex(q->mutex);

  if (!q->last_pkt)
    q->first_pkt = pkt1;
  else
    q->last_pkt->next = pkt1;
  q->last_pkt = pkt1;
  q->nb_packets++;
  q->size += pkt1->pkt.size;
  SDL_CondSignal(q->cond);
  
  SDL_UnlockMutex(q->mutex);
  return 0;
}
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
{
  AVPacketList *pkt1;
  int ret;

  SDL_LockMutex(q->mutex);
  
  for(;;) {
    
    if(global_video_state->quit) {
      ret = -1;
      break;
    }

    pkt1 = q->first_pkt;
    if (pkt1) {
      q->first_pkt = pkt1->next;
      if (!q->first_pkt)
	q->last_pkt = NULL;
      q->nb_packets--;
      q->size -= pkt1->pkt.size;
      *pkt = pkt1->pkt;
      av_free(pkt1);
      ret = 1;
      break;
    } else if (!block) {
      ret = 0;
      break;
    } else {
      SDL_CondWait(q->cond, q->mutex);
    }
  }
  SDL_UnlockMutex(q->mutex);
  return ret;
}

double get_audio_clock(VideoState *is) {
  double pts;
  int hw_buf_size, bytes_per_sec, n;
  
  pts = is->audio_clock; /* maintained in the audio thread */
  hw_buf_size = is->audio_buf_size - is->audio_buf_index;
  bytes_per_sec = 0;
  n = is->audio_ctx->channels * 2;
  if(is->audio_st) {
    bytes_per_sec = is->audio_ctx->sample_rate * n;
  }
  if(bytes_per_sec) {
    pts -= (double)hw_buf_size / bytes_per_sec;
  }
  return pts;
}
double get_video_clock(VideoState *is) {
  double delta;

  delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;
  return is->video_current_pts + delta;
}
double get_external_clock(VideoState *is) {
  return av_gettime() / 1000000.0;
}

double get_master_clock(VideoState *is) {
  if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
    return get_video_clock(is);
  } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
    return get_audio_clock(is);
  } else {
    return get_external_clock(is);
  }
}


/* Add or subtract samples to get a better sync, return new
   audio buffer size */
int synchronize_audio(VideoState *is, short *samples,
		      int samples_size, double pts) {
  int n;
  double ref_clock;

  n = 2 * is->audio_ctx->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);
	if(fabs(avg_diff) >= is->audio_diff_threshold) {
	  wanted_size = samples_size + ((int)(diff * is->audio_ctx->sample_rate) * n);
	  min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100);
	  max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100);
	  if(wanted_size < min_size) {
	    wanted_size = min_size;
	  } else if (wanted_size > max_size) {
	    wanted_size = max_size;
	  }
	  if(wanted_size < samples_size) {
	    /* remove samples */
	    samples_size = wanted_size;
	  } else if(wanted_size > samples_size) {
	    uint8_t *samples_end, *q;
	    int nb;

	    /* add samples by copying final sample*/
	    nb = (samples_size - wanted_size);
	    samples_end = (uint8_t *)samples + samples_size - n;
	    q = samples_end + n;
	    while(nb > 0) {
	      memcpy(q, samples_end, n);
	      q += n;
	      nb -= n;
	    }
	    samples_size = wanted_size;
	  }
	}
      }
    } else {
      /* difference is TOO big; reset diff stuff */
      is->audio_diff_avg_count = 0;
      is->audio_diff_cum = 0;
    }
  }
  return samples_size;
}

int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr) {

  int len1, data_size = 0;
  AVPacket *pkt = &is->audio_pkt;
  double pts;
  int n;

  for(;;) {
    while(is->audio_pkt_size > 0) {
      int got_frame = 0;
      len1 = avcodec_decode_audio4(is->audio_ctx, &is->audio_frame, &got_frame, pkt);
      if(len1 < 0) {
	/* if error, skip frame */
	is->audio_pkt_size = 0;
	break;
      }
      data_size = 0;
      if(got_frame) {
	data_size = av_samples_get_buffer_size(NULL, 
					       is->audio_ctx->channels,
					       is->audio_frame.nb_samples,
					       is->audio_ctx->sample_fmt,
					       1);
	assert(data_size <= buf_size);
	memcpy(audio_buf, is->audio_frame.data[0], data_size);
      }
      is->audio_pkt_data += len1;
      is->audio_pkt_size -= len1;
      if(data_size <= 0) {
	/* No data yet, get more frames */
	continue;
      }
      pts = is->audio_clock;
      *pts_ptr = pts;
      n = 2 * is->audio_ctx->channels;
      is->audio_clock += (double)data_size /
	(double)(n * is->audio_ctx->sample_rate);
      /* We have data, return it and come back for more later */
      return data_size;
    }
    if(pkt->data)
      av_free_packet(pkt);

    if(is->quit) {
      return -1;
    }
    /* next packet */
    if(packet_queue_get(&is->audioq, pkt, 1) < 0) {
      return -1;
    }
    is->audio_pkt_data = pkt->data;
    is->audio_pkt_size = pkt->size;
    /* if update, update the audio clock w/pts */
    if(pkt->pts != AV_NOPTS_VALUE) {
      is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
    }
  }
}

void audio_callback(void *userdata, Uint8 *stream, int len) {

  VideoState *is = (VideoState *)userdata;
  int len1, audio_size;
  double pts;

  while(len > 0) {
    if(is->audio_buf_index >= is->audio_buf_size) {
      /* We have already sent all our data; get more */
      audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts);
      if(audio_size < 0) {
	/* If error, output silence */
	is->audio_buf_size = 1024;
	memset(is->audio_buf, 0, is->audio_buf_size);
      } else {
	audio_size = synchronize_audio(is, (int16_t *)is->audio_buf,
				       audio_size, pts);
	is->audio_buf_size = audio_size;
      }
      is->audio_buf_index = 0;
    }
    len1 = is->audio_buf_size - is->audio_buf_index;
    if(len1 > len)
      len1 = len;
    memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
    len -= len1;
    stream += len1;
    is->audio_buf_index += len1;
  }
}

static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {
  SDL_Event event;
  event.type = FF_REFRESH_EVENT;
  event.user.data1 = opaque;
  SDL_PushEvent(&event);
  return 0; /* 0 means stop timer */
}

/* schedule a video refresh in 'delay' ms */
static void schedule_refresh(VideoState *is, int delay) {
  SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
}

void video_display(VideoState *is) {

  SDL_Rect rect;
  VideoPicture *vp;
  float aspect_ratio;
  int w, h, x, y;
  int i;

  vp = &is->pictq[is->pictq_rindex];
  if(vp->bmp) {
    if(is->video_ctx->sample_aspect_ratio.num == 0) {
      aspect_ratio = 0;
    } else {
      aspect_ratio = av_q2d(is->video_ctx->sample_aspect_ratio) *
	is->video_ctx->width / is->video_ctx->height;
    }
    if(aspect_ratio <= 0.0) {
      aspect_ratio = (float)is->video_ctx->width /
	(float)is->video_ctx->height;
    }
    h = screen->h;
    w = ((int)rint(h * aspect_ratio)) & -3;
    if(w > screen->w) {
      w = screen->w;
      h = ((int)rint(w / aspect_ratio)) & -3;
    }
    x = (screen->w - w) / 2;
    y = (screen->h - h) / 2;
    
    rect.x = x;
    rect.y = y;
    rect.w = w;
    rect.h = h;
    SDL_LockMutex(screen_mutex);
    SDL_DisplayYUVOverlay(vp->bmp, &rect);
    SDL_UnlockMutex(screen_mutex);
  }
}

void video_refresh_timer(void *userdata) {

  VideoState *is = (VideoState *)userdata;
  VideoPicture *vp;
  double actual_delay, delay, sync_threshold, ref_clock, diff;
  
  if(is->video_st) {
    if(is->pictq_size == 0) {
      schedule_refresh(is, 1);
    } else {
      vp = &is->pictq[is->pictq_rindex];
      
      is->video_current_pts = vp->pts;
      is->video_current_pts_time = av_gettime();
      delay = vp->pts - is->frame_last_pts; /* the pts from last time */
      if(delay <= 0 || delay >= 1.0) {
	/* if incorrect delay, use previous one */
	delay = is->frame_last_delay;
      }
      /* save for next time */
      is->frame_last_delay = delay;
      is->frame_last_pts = vp->pts;



      /* update delay to sync to audio if not master source */
      if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) {
	ref_clock = get_master_clock(is);
	diff = vp->pts - ref_clock;
	
	/* Skip or repeat the frame. Take delay into account
	   FFPlay still doesn't "know if this is the best guess." */
	sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;
	if(fabs(diff) < AV_NOSYNC_THRESHOLD) {
	  if(diff <= -sync_threshold) {
	    delay = 0;
	  } else if(diff >= sync_threshold) {
	    delay = 2 * delay;
	  }
	}
      }
      is->frame_timer += delay;
      /* computer the REAL delay */
      actual_delay = is->frame_timer - (av_gettime() / 1000000.0);
      if(actual_delay < 0.010) {
	/* Really it should skip the picture instead */
	actual_delay = 0.010;
      }
      schedule_refresh(is, (int)(actual_delay * 1000 + 0.5));
      
      /* show the picture! */
      video_display(is);
      
      /* update queue for next picture! */
      if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) {
	is->pictq_rindex = 0;
      }
      SDL_LockMutex(is->pictq_mutex);
      is->pictq_size--;
      SDL_CondSignal(is->pictq_cond);
      SDL_UnlockMutex(is->pictq_mutex);
    }
  } else {
    schedule_refresh(is, 100);
  }
}
      
void alloc_picture(void *userdata) {

  VideoState *is = (VideoState *)userdata;
  VideoPicture *vp;

  vp = &is->pictq[is->pictq_windex];
  if(vp->bmp) {
    // we already have one make another, bigger/smaller
    SDL_FreeYUVOverlay(vp->bmp);
  }
  // Allocate a place to put our YUV image on that screen
  SDL_LockMutex(screen_mutex);
  vp->bmp = SDL_CreateYUVOverlay(is->video_ctx->width,
				 is->video_ctx->height,
				 SDL_YV12_OVERLAY,
				 screen);
  SDL_UnlockMutex(screen_mutex);

  vp->width = is->video_ctx->width;
  vp->height = is->video_ctx->height;
  vp->allocated = 1;

}

int queue_picture(VideoState *is, AVFrame *pFrame, double pts) {

  VideoPicture *vp;
  int dst_pix_fmt;
  AVPicture pict;

  /* wait until we have space for a new pic */
  SDL_LockMutex(is->pictq_mutex);
  while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
	!is->quit) {
    SDL_CondWait(is->pictq_cond, is->pictq_mutex);
  }
  SDL_UnlockMutex(is->pictq_mutex);

  if(is->quit)
    return -1;

  // windex is set to 0 initially
  vp = &is->pictq[is->pictq_windex];

  /* allocate or resize the buffer! */
  if(!vp->bmp ||
     vp->width != is->video_ctx->width ||
     vp->height != is->video_ctx->height) {
    SDL_Event event;

    vp->allocated = 0;
    alloc_picture(is);
    if(is->quit) {
      return -1;
    }
  }

  /* We have a place to put our picture on the queue */

  if(vp->bmp) {

    SDL_LockYUVOverlay(vp->bmp);
    vp->pts = pts;
    
    dst_pix_fmt = PIX_FMT_YUV420P;
    /* point pict at the queue */

    pict.data[0] = vp->bmp->pixels[0];
    pict.data[1] = vp->bmp->pixels[2];
    pict.data[2] = vp->bmp->pixels[1];
    
    pict.linesize[0] = vp->bmp->pitches[0];
    pict.linesize[1] = vp->bmp->pitches[2];
    pict.linesize[2] = vp->bmp->pitches[1];
    
    // Convert the image into YUV format that SDL uses
    sws_scale(is->sws_ctx, (uint8_t const * const *)pFrame->data,
	      pFrame->linesize, 0, is->video_ctx->height,
	      pict.data, pict.linesize);
    
    SDL_UnlockYUVOverlay(vp->bmp);
    /* now we inform our display thread that we have a pic ready */
    if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) {
      is->pictq_windex = 0;
    }
    SDL_LockMutex(is->pictq_mutex);
    is->pictq_size++;
    SDL_UnlockMutex(is->pictq_mutex);
  }
  return 0;
}

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_ctx->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;
}

int video_thread(void *arg) {
  VideoState *is = (VideoState *)arg;
  AVPacket pkt1, *packet = &pkt1;
  int frameFinished;
  AVFrame *pFrame;
  double pts;

  pFrame = av_frame_alloc();

  for(;;) {
    if(packet_queue_get(&is->videoq, packet, 1) < 0) {
      // means we quit getting packets
      break;
    }
    if(packet_queue_get(&is->videoq, packet, 1) < 0) {
      // means we quit getting packets
      break;
    }
    pts = 0;

    // Decode video frame
    avcodec_decode_video2(is->video_ctx, pFrame, &frameFinished, packet);

    if((pts = av_frame_get_best_effort_timestamp(pFrame)) == AV_NOPTS_VALUE) {
    } else {
      pts = 0;
    }
    pts *= av_q2d(is->video_st->time_base);

    // Did we get a video frame?
    if(frameFinished) {
      pts = synchronize_video(is, pFrame, pts);
      if(queue_picture(is, pFrame, pts) < 0) {
	break;
      }
    }
    av_free_packet(packet);
  }
  av_frame_free(&pFrame);
  return 0;
}

int stream_component_open(VideoState *is, int stream_index) {

  AVFormatContext *pFormatCtx = is->pFormatCtx;
  AVCodecContext *codecCtx = NULL;
  AVCodec *codec = NULL;
  SDL_AudioSpec wanted_spec, spec;

  if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) {
    return -1;
  }

  codec = avcodec_find_decoder(pFormatCtx->streams[stream_index]->codec->codec_id);
  if(!codec) {
    fprintf(stderr, "Unsupported codec!\n");
    return -1;
  }

  codecCtx = avcodec_alloc_context3(codec);
  if(avcodec_copy_context(codecCtx, pFormatCtx->streams[stream_index]->codec) != 0) {
    fprintf(stderr, "Couldn't copy codec context");
    return -1; // Error copying codec context
  }


  if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {
    // Set audio settings from codec info
    wanted_spec.freq = codecCtx->sample_rate;
    wanted_spec.format = AUDIO_S16SYS;
    wanted_spec.channels = codecCtx->channels;
    wanted_spec.silence = 0;
    wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
    wanted_spec.callback = audio_callback;
    wanted_spec.userdata = is;
    
    if(SDL_OpenAudio(&wanted_spec, &spec) < 0) {
      fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
      return -1;
    }
    is->audio_hw_buf_size = spec.size;
  }
  if(avcodec_open2(codecCtx, codec, NULL) < 0) {
    fprintf(stderr, "Unsupported codec!\n");
    return -1;
  }

  switch(codecCtx->codec_type) {
  case AVMEDIA_TYPE_AUDIO:
    is->audioStream = stream_index;
    is->audio_st = pFormatCtx->streams[stream_index];
    is->audio_ctx = codecCtx;
    is->audio_buf_size = 0;
    is->audio_buf_index = 0;
    memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
    packet_queue_init(&is->audioq);
    SDL_PauseAudio(0);
    break;
  case AVMEDIA_TYPE_VIDEO:
    is->videoStream = stream_index;
    is->video_st = pFormatCtx->streams[stream_index];
    is->video_ctx = codecCtx;

    is->frame_timer = (double)av_gettime() / 1000000.0;
    is->frame_last_delay = 40e-3;
    is->video_current_pts_time = av_gettime();

    packet_queue_init(&is->videoq);
    is->video_tid = SDL_CreateThread(video_thread, is);
    is->sws_ctx = sws_getContext(is->video_ctx->width, is->video_ctx->height,
				 is->video_ctx->pix_fmt, is->video_ctx->width,
				 is->video_ctx->height, PIX_FMT_YUV420P,
				 SWS_BILINEAR, NULL, NULL, NULL
				 );
    break;
  default:
    break;
  }
}

int decode_thread(void *arg) {

  VideoState *is = (VideoState *)arg;
  AVFormatContext *pFormatCtx;
  AVPacket pkt1, *packet = &pkt1;

  int video_index = -1;
  int audio_index = -1;
  int i;

  is->videoStream=-1;
  is->audioStream=-1;

  global_video_state = is;

  // Open video file
  if(avformat_open_input(&pFormatCtx, is->filename, NULL, NULL)!=0)
    return -1; // Couldn't open file

  is->pFormatCtx = pFormatCtx;
  
  // Retrieve stream information
  if(avformat_find_stream_info(pFormatCtx, NULL)<0)
    return -1; // Couldn't find stream information
  
  // Dump information about file onto standard error
  av_dump_format(pFormatCtx, 0, is->filename, 0);
  
  // Find the first video stream

  for(i=0; i<pFormatCtx->nb_streams; i++) {
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO &&
       video_index < 0) {
      video_index=i;
    }
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
       audio_index < 0) {
      audio_index=i;
    }
  }
  if(audio_index >= 0) {
    stream_component_open(is, audio_index);
  }
  if(video_index >= 0) {
    stream_component_open(is, video_index);
  }   

  if(is->videoStream < 0 || is->audioStream < 0) {
    fprintf(stderr, "%s: could not open codecs\n", is->filename);
    goto fail;
  }

  // main decode loop

  for(;;) {
    if(is->quit) {
      break;
    }
    // seek stuff goes here
    if(is->audioq.size > MAX_AUDIOQ_SIZE ||
       is->videoq.size > MAX_VIDEOQ_SIZE) {
      SDL_Delay(10);
      continue;
    }
    if(av_read_frame(is->pFormatCtx, packet) < 0) {
      if(is->pFormatCtx->pb->error == 0) {
	SDL_Delay(100); /* no error; wait for user input */
	continue;
      } else {
	break;
      }
    }
    // Is this a packet from the video stream?
    if(packet->stream_index == is->videoStream) {
      packet_queue_put(&is->videoq, packet);
    } else if(packet->stream_index == is->audioStream) {
      packet_queue_put(&is->audioq, packet);
    } else {
      av_free_packet(packet);
    }
  }
  /* all done - wait for it */
  while(!is->quit) {
    SDL_Delay(100);
  }

 fail:
  if(1){
    SDL_Event event;
    event.type = FF_QUIT_EVENT;
    event.user.data1 = is;
    SDL_PushEvent(&event);
  }
  return 0;
}

int main(int argc, char *argv[]) {

  SDL_Event       event;

  VideoState      *is;

  is = av_mallocz(sizeof(VideoState));

  if(argc < 2) {
    fprintf(stderr, "Usage: test <file>\n");
    exit(1);
  }
  // Register all formats and codecs
  av_register_all();
  
  if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
    fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
    exit(1);
  }

  // Make a screen to put our video
#ifndef __DARWIN__
        screen = SDL_SetVideoMode(640, 480, 0, 0);
#else
        screen = SDL_SetVideoMode(640, 480, 24, 0);
#endif
  if(!screen) {
    fprintf(stderr, "SDL: could not set video mode - exiting\n");
    exit(1);
  }

  screen_mutex = SDL_CreateMutex();

  av_strlcpy(is->filename, argv[1], sizeof(is->filename));

  is->pictq_mutex = SDL_CreateMutex();
  is->pictq_cond = SDL_CreateCond();

  schedule_refresh(is, 40);

  is->av_sync_type = DEFAULT_AV_SYNC_TYPE;
  is->parse_tid = SDL_CreateThread(decode_thread, is);
  if(!is->parse_tid) {
    av_free(is);
    return -1;
  }
  for(;;) {

    SDL_WaitEvent(&event);
    switch(event.type) {
    case FF_QUIT_EVENT:
    case SDL_QUIT:
      is->quit = 1;
      SDL_Quit();
      return 0;
      break;
    case FF_REFRESH_EVENT:
      video_refresh_timer(event.user.data1);
      break;
    default:
      break;
    }
  }
  return 0;
}


Agora que temos um reprodutor mais ou menos decente, no qual você pode assistir a um filme, agora vamos sobreviver. Na última vez, tocamos um pouco na sincronização, ou seja, sincronização do som com o vídeo, nessa ordem, e não vice-versa. Vamos criar o mesmo layout do vídeo: crie um relógio de vídeo interno para rastrear a distância do fluxo de vídeo e sincronizar o áudio com ele. Mais tarde generalizaremos ainda mais - sincronizamos áudio e vídeo com um relógio externo.

Implementação de relógio de vídeo


Agora, queremos criar um relógio de vídeo semelhante ao relógio de áudio da última vez: um valor interno que retorna o deslocamento de tempo atual do vídeo atualmente sendo reproduzido. Você pode pensar que será tão simples quanto atualizar o cronômetro com o PTS atual do último quadro exibido. No entanto, não esqueça que o tempo entre os quadros de vídeo pode ser muito longo se cairmos no nível de milissegundos. Portanto, a solução é rastrear outro valor, o horário em que configuramos o relógio de vídeo no PTS do último quadro. Portanto, o valor atual do relógio de vídeo será PTS_of_last_frame + ( current_time - time_elapsed_since_PTS_value_was_set) Esta solução é muito semelhante à que fizemos com o get_audio_clock .

Portanto, em nossa estrutura completa, colocaremos double video_current_pts e int64_t video_current_pts_time . O relógio será atualizado na função video_refresh_timer :

void video_refresh_timer(void *userdata) {

  /* ... */

  if(is->video_st) {
    if(is->pictq_size == 0) {
      schedule_refresh(is, 1);
    } else {
      vp = &is->pictq[is->pictq_rindex];

      is->video_current_pts = vp->pts;
      is->video_current_pts_time = av_gettime();

Não esqueça de inicializá-lo em stream_component_open :

is->video_current_pts_time = av_gettime();

E agora tudo o que precisamos é de alguma maneira de obter as informações:

double get_video_clock(VideoState *is) {
  double delta;

  delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;
  return is->video_current_pts + delta;
}

Abstraindo do relógio


Mas por que se forçar a usar um relógio de vídeo? Você pode ir além e alterar nosso código de sincronização de vídeo para que áudio e vídeo não tentem sincronizar um ao outro. Imagine como será uma bagunça se tentarmos fazer isso com uma opção de linha de comando, como no FFplay. Então, vamos abstrato longe : vamos criar uma nova função de invólucro, get_master_clock , que verifica a variável av_sync_type , e em seguida, chama get_audio_clock , get_video_clock, ou qualquer outro relógio que poderia usá-lo. Podemos até usar um relógio de computador, que chamamos de get_external_clock :

enum {
  AV_SYNC_AUDIO_MASTER,
  AV_SYNC_VIDEO_MASTER,
  AV_SYNC_EXTERNAL_MASTER,
};

#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER

double get_master_clock(VideoState *is) {
  if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
    return get_video_clock(is);
  } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
    return get_audio_clock(is);
  } else {
    return get_external_clock(is);
  }
}
main() {
...
  is->av_sync_type = DEFAULT_AV_SYNC_TYPE;
...
}

Sincronização de áudio


Agora a parte mais difícil: sincronizar o áudio com o relógio de vídeo. Nossa estratégia é medir onde está o áudio, compará-lo com o relógio de vídeo e descobrir quantas amostras precisamos ajustar, ou seja, precisamos acelerar deixando cair as amostras ou diminuir a velocidade adicionando?

Executamos a função synchronize_audio toda vez que processamos cada conjunto de amostras de áudio que obtemos para reduzir ou aumentar adequadamente esse conjunto. No entanto, não queremos sincronizar o tempo todo, porque o processamento de áudio ocorre com muito mais frequência do que o processamento de pacotes de vídeo. Então, vamos definir o número mínimo de chamadas consecutivas para a função synchronize_audioque são considerados não sincronizados antes de nos preocuparmos em fazer qualquer coisa. Obviamente, como da última vez, “dessincronização” significa que o relógio de áudio e o relógio de vídeo diferem em uma quantidade maior que o limite de sincronização.

Então, vamos usar um coeficiente fracionário, digamos, s , e agora, digamos que temos Nconjuntos de amostras de áudio que estavam fora de sincronia. O número de amostras que não sincronizamos também pode variar bastante; portanto, assumimos o valor médio de quanto cada uma delas não está sincronizada. Por exemplo, a primeira chamada pode mostrar que não estamos sincronizados por 40 ms, a próxima por 50 ms e assim por diante. Mas não vamos ter uma média simples, porque os valores mais recentes são mais importantes do que aqueles que vêm antes deles. Então, vamos usar um coeficiente fracionário, digamos, c , e resumir as diferenças da seguinte forma: diff_sum = new_diff + diff_sum * c . Quando estamos prontos para encontrar a diferença média, simplesmente calculamos avg_diff =diff_sum * (1 - c ).

Que diabos está acontecendo aqui? A equação parece algum tipo de mágica. Bem, essa é basicamente uma média ponderada usando uma série geométrica como pesos. Não sei se existe um nome para isso (chequei até na Wikipedia!). Mas, para mais informações, aqui está uma explicação (ou aqui: weightedmean.txt ).

Aqui está a aparência da nossa função:

/* Add or subtract samples to get a better sync, return new
   audio buffer size */
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;
}

Então está tudo bem conosco; sabemos aproximadamente o quanto o som não é consistente com o vídeo ou com o que usamos como um relógio. Então, agora vamos calcular quantas amostras precisamos adicionar ou descartar colocando esse código na seção "Reduzindo / expandindo o código do buffer":

if(fabs(avg_diff) >= is->audio_diff_threshold) {
  wanted_size = samples_size + 
  ((int)(diff * is->audio_st->codec->sample_rate) * n);
  min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX)
                             / 100);
  max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) 
                             / 100);
  if(wanted_size < min_size) {
    wanted_size = min_size;
  } else if (wanted_size > max_size) {
    wanted_size = max_size;
  }

Lembre-se de que audio_length * ( sample_rate * # do canal * 2) é o número de amostras em audio_length segundos de áudio. Portanto, o número de amostras que queremos será igual ao número de amostras que já temos, mais ou menos o número de amostras que correspondem à quantidade de tempo durante o qual o som foi reproduzido. Também definiremos um limite de quão grande ou pequena nossa correção pode ser, porque se alterarmos muito nosso buffer, será muito chato para o usuário.

Correção do número de amostras


Agora precisamos corrigir o som. Você deve ter notado que nossa função synchronize_audio retorna um tamanho de amostra, que nos informa quantos bytes enviar para o fluxo. Então, precisamos apenas ajustar o tamanho da amostra para o valor desejado. Isso funciona para reduzir o tamanho da amostra. Mas se você precisar aumentá-lo, não podemos apenas aumentar o tamanho da amostra, porque não há mais dados no buffer! Portanto, devemos adicionar um pouco. Mas o que exatamente adicionar? Seria tolice tentar extrapolar o áudio, então vamos apenas usar o áudio que já temos, adicionando o valor da última amostra ao buffer.

if(wanted_size < samples_size) {
  /* remove samples */
  samples_size = wanted_size;
} else if(wanted_size > samples_size) {
  uint8_t *samples_end, *q;
  int nb;

  /* add samples by copying final samples */
  nb = (samples_size - wanted_size);
  samples_end = (uint8_t *)samples + samples_size - n;
  q = samples_end + n;
  while(nb > 0) {
    memcpy(q, samples_end, n);
    q += n;
    nb -= n;
  }
  samples_size = wanted_size;
}

Agora estamos retornando o tamanho da amostra e terminamos com essa função. Tudo o que precisamos fazer agora é usar isso:

void audio_callback(void *userdata, Uint8 *stream, int len) {

  VideoState *is = (VideoState *)userdata;
  int len1, audio_size;
  double pts;

  while(len > 0) {
    if(is->audio_buf_index >= is->audio_buf_size) {
      /* We have already sent all our data; get more */
      audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts);
      if(audio_size < 0) {
	/* If error, output silence */
	is->audio_buf_size = 1024;
	memset(is->audio_buf, 0, is->audio_buf_size);
      } else {
	audio_size = synchronize_audio(is, (int16_t *)is->audio_buf,
				       audio_size, pts);
	is->audio_buf_size = audio_size;

Tudo o que fizemos foi inserir uma chamada synchronize_audio . (Além disso, verifique o código-fonte, no qual inicializamos as variáveis ​​que eu não me incomodei em definir.)

E a última, antes de terminar: precisamos adicionar a condição "if" para garantir que não sincronizamos o vídeo se for o relógio principal:

if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) {
  ref_clock = get_master_clock(is);
  diff = vp->pts - ref_clock;

  /* Skip or repeat the frame. Take delay into account
     FFPlay still doesn't "know if this is the best guess." */
  sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay :
                    AV_SYNC_THRESHOLD;
  if(fabs(diff) < AV_NOSYNC_THRESHOLD) {
    if(diff <= -sync_threshold) {
      delay = 0;
    } else if(diff >= sync_threshold) {
      delay = 2 * delay;
    }
  }
}

E funciona! Verifique o arquivo de origem para inicializar todas as variáveis ​​que eu não me incomodei em definir ou inicializar. Em seguida, compile:

gcc -o tutorial06 tutorial06.c -lavutil -lavformat -lavcodec -lswscale -lz -lm \
`sdl-config --cflags --libs`

e o vôo será normal.

Na última lição, retrocederemos.






Lição 7: pesquisa


Lista completa tutorial07.c
// tutorial05.c
// A pedagogical video player that really works!
//
// Code based on FFplay, Copyright (c) 2003 Fabrice Bellard, 
// and a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
// With updates from https://github.com/chelyaev/ffmpeg-tutorial
// Updates tested on:
// LAVC 54.59.100, LAVF 54.29.104, LSWS 2.1.101, SDL 1.2.15
// on GCC 4.7.2 in Debian February 2015
// Use
//
// gcc -o tutorial05 tutorial05.c -lavformat -lavcodec -lswscale -lz -lm `sdl-config --cflags --libs`
// to build (assuming libavformat and libavcodec are correctly installed, 
// and assuming you have sdl-config. Please refer to SDL docs for your installation.)
//
// Run using
// tutorial04 myvideofile.mpg
//
// to play the video stream on your screen.

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <SDL.h>
#include <SDL_thread.h>

#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif

#include <stdio.h>
#include <assert.h>
#include <math.h>

// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif

#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000

#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)

#define AV_SYNC_THRESHOLD 0.01
#define AV_NOSYNC_THRESHOLD 10.0

#define SAMPLE_CORRECTION_PERCENT_MAX 10
#define AUDIO_DIFF_AVG_NB 20

#define FF_REFRESH_EVENT (SDL_USEREVENT)
#define FF_QUIT_EVENT (SDL_USEREVENT + 1)

#define VIDEO_PICTURE_QUEUE_SIZE 1

#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER

typedef struct PacketQueue {
  AVPacketList *first_pkt, *last_pkt;
  int nb_packets;
  int size;
  SDL_mutex *mutex;
  SDL_cond *cond;
} PacketQueue;


typedef struct VideoPicture {
  SDL_Overlay *bmp;
  int width, height; /* source height & width */
  int allocated;
  double pts;
} VideoPicture;

typedef struct VideoState {

  AVFormatContext *pFormatCtx;
  int             videoStream, audioStream;

  int             av_sync_type;
  double          external_clock; /* external clock base */
  int64_t         external_clock_time;
  int             seek_req;
  int             seek_flags;
  int64_t         seek_pos;

  double          audio_clock;
  AVStream        *audio_st;
  AVCodecContext  *audio_ctx;
  PacketQueue     audioq;
  uint8_t         audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
  unsigned int    audio_buf_size;
  unsigned int    audio_buf_index;
  AVFrame         audio_frame;
  AVPacket        audio_pkt;
  uint8_t         *audio_pkt_data;
  int             audio_pkt_size;
  int             audio_hw_buf_size;
  double          audio_diff_cum; /* used for AV difference average computation */
  double          audio_diff_avg_coef;
  double          audio_diff_threshold;
  int             audio_diff_avg_count;
  double          frame_timer;
  double          frame_last_pts;
  double          frame_last_delay;
  double          video_clock; ///<pts of last decoded frame / predicted pts of next decoded frame
  double          video_current_pts; ///<current displayed pts (different from video_clock if frame fifos are used)
  int64_t         video_current_pts_time;  ///<time (av_gettime) at which we updated video_current_pts - used to have running video pts
  AVStream        *video_st;
  AVCodecContext  *video_ctx;
  PacketQueue     videoq;
  struct SwsContext *sws_ctx;

  VideoPicture    pictq[VIDEO_PICTURE_QUEUE_SIZE];
  int             pictq_size, pictq_rindex, pictq_windex;
  SDL_mutex       *pictq_mutex;
  SDL_cond        *pictq_cond;
  
  SDL_Thread      *parse_tid;
  SDL_Thread      *video_tid;

  char            filename[1024];
  int             quit;
} VideoState;

enum {
  AV_SYNC_AUDIO_MASTER,
  AV_SYNC_VIDEO_MASTER,
  AV_SYNC_EXTERNAL_MASTER,
};

SDL_Surface     *screen;
SDL_mutex       *screen_mutex;

/* Since we only have one decoding thread, the Big Struct
   can be global in case we need it. */
VideoState *global_video_state;
AVPacket flush_pkt;

void packet_queue_init(PacketQueue *q) {
  memset(q, 0, sizeof(PacketQueue));
  q->mutex = SDL_CreateMutex();
  q->cond = SDL_CreateCond();
}

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

  AVPacketList *pkt1;
  if(pkt != &flush_pkt && av_dup_packet(pkt) < 0) {
    return -1;
  }
  pkt1 = av_malloc(sizeof(AVPacketList));
  if (!pkt1)
    return -1;
  pkt1->pkt = *pkt;
  pkt1->next = NULL;
  
  SDL_LockMutex(q->mutex);

  if (!q->last_pkt)
    q->first_pkt = pkt1;
  else
    q->last_pkt->next = pkt1;
  q->last_pkt = pkt1;
  q->nb_packets++;
  q->size += pkt1->pkt.size;
  SDL_CondSignal(q->cond);
  
  SDL_UnlockMutex(q->mutex);
  return 0;
}
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
{
  AVPacketList *pkt1;
  int ret;

  SDL_LockMutex(q->mutex);
  
  for(;;) {
    
    if(global_video_state->quit) {
      ret = -1;
      break;
    }

    pkt1 = q->first_pkt;
    if (pkt1) {
      q->first_pkt = pkt1->next;
      if (!q->first_pkt)
	q->last_pkt = NULL;
      q->nb_packets--;
      q->size -= pkt1->pkt.size;
      *pkt = pkt1->pkt;
      av_free(pkt1);
      ret = 1;
      break;
    } else if (!block) {
      ret = 0;
      break;
    } else {
      SDL_CondWait(q->cond, q->mutex);
    }
  }
  SDL_UnlockMutex(q->mutex);
  return ret;
}

static void packet_queue_flush(PacketQueue *q) {
  AVPacketList *pkt, *pkt1;

  SDL_LockMutex(q->mutex);
  for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
    pkt1 = pkt->next;
    av_free_packet(&pkt->pkt);
    av_freep(&pkt);
  }
  q->last_pkt = NULL;
  q->first_pkt = NULL;
  q->nb_packets = 0;
  q->size = 0;
  SDL_UnlockMutex(q->mutex);
}

double get_audio_clock(VideoState *is) {
  double pts;
  int hw_buf_size, bytes_per_sec, n;
  
  pts = is->audio_clock; /* maintained in the audio thread */
  hw_buf_size = is->audio_buf_size - is->audio_buf_index;
  bytes_per_sec = 0;
  n = is->audio_ctx->channels * 2;
  if(is->audio_st) {
    bytes_per_sec = is->audio_ctx->sample_rate * n;
  }
  if(bytes_per_sec) {
    pts -= (double)hw_buf_size / bytes_per_sec;
  }
  return pts;
}
double get_video_clock(VideoState *is) {
  double delta;

  delta = (av_gettime() - is->video_current_pts_time) / 1000000.0;
  return is->video_current_pts + delta;
}
double get_external_clock(VideoState *is) {
  return av_gettime() / 1000000.0;
}

double get_master_clock(VideoState *is) {
  if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
    return get_video_clock(is);
  } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
    return get_audio_clock(is);
  } else {
    return get_external_clock(is);
  }
}


/* Add or subtract samples to get a better sync, return new
   audio buffer size */
int synchronize_audio(VideoState *is, short *samples,
		      int samples_size, double pts) {
  int n;
  double ref_clock;

  n = 2 * is->audio_ctx->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);
	if(fabs(avg_diff) >= is->audio_diff_threshold) {
	  wanted_size = samples_size + ((int)(diff * is->audio_ctx->sample_rate) * n);
	  min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100);
	  max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100);
	  if(wanted_size < min_size) {
	    wanted_size = min_size;
	  } else if (wanted_size > max_size) {
	    wanted_size = max_size;
	  }
	  if(wanted_size < samples_size) {
	    /* remove samples */
	    samples_size = wanted_size;
	  } else if(wanted_size > samples_size) {
	    uint8_t *samples_end, *q;
	    int nb;

	    /* add samples by copying final sample*/
	    nb = (samples_size - wanted_size);
	    samples_end = (uint8_t *)samples + samples_size - n;
	    q = samples_end + n;
	    while(nb > 0) {
	      memcpy(q, samples_end, n);
	      q += n;
	      nb -= n;
	    }
	    samples_size = wanted_size;
	  }
	}
      }
    } else {
      /* difference is TOO big; reset diff stuff */
      is->audio_diff_avg_count = 0;
      is->audio_diff_cum = 0;
    }
  }
  return samples_size;
}

int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr) {

  int len1, data_size = 0;
  AVPacket *pkt = &is->audio_pkt;
  double pts;
  int n;

  for(;;) {
    while(is->audio_pkt_size > 0) {
      int got_frame = 0;
      len1 = avcodec_decode_audio4(is->audio_ctx, &is->audio_frame, &got_frame, pkt);
      if(len1 < 0) {
	/* if error, skip frame */
	is->audio_pkt_size = 0;
	break;
      }
      data_size = 0;
      if(got_frame) {
	data_size = av_samples_get_buffer_size(NULL, 
					       is->audio_ctx->channels,
					       is->audio_frame.nb_samples,
					       is->audio_ctx->sample_fmt,
					       1);
	assert(data_size <= buf_size);
	memcpy(audio_buf, is->audio_frame.data[0], data_size);
      }
      is->audio_pkt_data += len1;
      is->audio_pkt_size -= len1;
      if(data_size <= 0) {
	/* No data yet, get more frames */
	continue;
      }
      pts = is->audio_clock;
      *pts_ptr = pts;
      n = 2 * is->audio_ctx->channels;
      is->audio_clock += (double)data_size /
	(double)(n * is->audio_ctx->sample_rate);
      /* We have data, return it and come back for more later */
      return data_size;
    }
    if(pkt->data)
      av_free_packet(pkt);

    if(is->quit) {
      return -1;
    }
    /* next packet */
    if(packet_queue_get(&is->audioq, pkt, 1) < 0) {
      return -1;
    }
    if(pkt->data == flush_pkt.data) {
      avcodec_flush_buffers(is->audio_ctx);
      continue;
    }
    is->audio_pkt_data = pkt->data;
    is->audio_pkt_size = pkt->size;
    /* if update, update the audio clock w/pts */
    if(pkt->pts != AV_NOPTS_VALUE) {
      is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
    }
  }
}

void audio_callback(void *userdata, Uint8 *stream, int len) {

  VideoState *is = (VideoState *)userdata;
  int len1, audio_size;
  double pts;

  while(len > 0) {
    if(is->audio_buf_index >= is->audio_buf_size) {
      /* We have already sent all our data; get more */
      audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts);
      if(audio_size < 0) {
	/* If error, output silence */
	is->audio_buf_size = 1024;
	memset(is->audio_buf, 0, is->audio_buf_size);
      } else {
	audio_size = synchronize_audio(is, (int16_t *)is->audio_buf,
				       audio_size, pts);
	is->audio_buf_size = audio_size;
      }
      is->audio_buf_index = 0;
    }
    len1 = is->audio_buf_size - is->audio_buf_index;
    if(len1 > len)
      len1 = len;
    memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
    len -= len1;
    stream += len1;
    is->audio_buf_index += len1;
  }
}

static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {
  SDL_Event event;
  event.type = FF_REFRESH_EVENT;
  event.user.data1 = opaque;
  SDL_PushEvent(&event);
  return 0; /* 0 means stop timer */
}

/* schedule a video refresh in 'delay' ms */
static void schedule_refresh(VideoState *is, int delay) {
  SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
}

void video_display(VideoState *is) {

  SDL_Rect rect;
  VideoPicture *vp;
  float aspect_ratio;
  int w, h, x, y;
  int i;

  vp = &is->pictq[is->pictq_rindex];
  if(vp->bmp) {
    if(is->video_ctx->sample_aspect_ratio.num == 0) {
      aspect_ratio = 0;
    } else {
      aspect_ratio = av_q2d(is->video_ctx->sample_aspect_ratio) *
	is->video_ctx->width / is->video_ctx->height;
    }
    if(aspect_ratio <= 0.0) {
      aspect_ratio = (float)is->video_ctx->width /
	(float)is->video_ctx->height;
    }
    h = screen->h;
    w = ((int)rint(h * aspect_ratio)) & -3;
    if(w > screen->w) {
      w = screen->w;
      h = ((int)rint(w / aspect_ratio)) & -3;
    }
    x = (screen->w - w) / 2;
    y = (screen->h - h) / 2;
    
    rect.x = x;
    rect.y = y;
    rect.w = w;
    rect.h = h;
    SDL_LockMutex(screen_mutex);
    SDL_DisplayYUVOverlay(vp->bmp, &rect);
    SDL_UnlockMutex(screen_mutex);
  }
}

void video_refresh_timer(void *userdata) {

  VideoState *is = (VideoState *)userdata;
  VideoPicture *vp;
  double actual_delay, delay, sync_threshold, ref_clock, diff;
  
  if(is->video_st) {
    if(is->pictq_size == 0) {
      schedule_refresh(is, 1);
    } else {
      vp = &is->pictq[is->pictq_rindex];
      
      is->video_current_pts = vp->pts;
      is->video_current_pts_time = av_gettime();
      delay = vp->pts - is->frame_last_pts; /* the pts from last time */
      if(delay <= 0 || delay >= 1.0) {
	/* if incorrect delay, use previous one */
	delay = is->frame_last_delay;
      }
      /* save for next time */
      is->frame_last_delay = delay;
      is->frame_last_pts = vp->pts;



      /* update delay to sync to audio if not master source */
      if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) {
	ref_clock = get_master_clock(is);
	diff = vp->pts - ref_clock;
	
	/* Skip or repeat the frame. Take delay into account
	   FFPlay still doesn't "know if this is the best guess." */
	sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;
	if(fabs(diff) < AV_NOSYNC_THRESHOLD) {
	  if(diff <= -sync_threshold) {
	    delay = 0;
	  } else if(diff >= sync_threshold) {
	    delay = 2 * delay;
	  }
	}
      }
      is->frame_timer += delay;
      /* computer the REAL delay */
      actual_delay = is->frame_timer - (av_gettime() / 1000000.0);
      if(actual_delay < 0.010) {
	/* Really it should skip the picture instead */
	actual_delay = 0.010;
      }
      schedule_refresh(is, (int)(actual_delay * 1000 + 0.5));
      
      /* show the picture! */
      video_display(is);
      
      /* update queue for next picture! */
      if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) {
	is->pictq_rindex = 0;
      }
      SDL_LockMutex(is->pictq_mutex);
      is->pictq_size--;
      SDL_CondSignal(is->pictq_cond);
      SDL_UnlockMutex(is->pictq_mutex);
    }
  } else {
    schedule_refresh(is, 100);
  }
}
      
void alloc_picture(void *userdata) {

  VideoState *is = (VideoState *)userdata;
  VideoPicture *vp;

  vp = &is->pictq[is->pictq_windex];
  if(vp->bmp) {
    // we already have one make another, bigger/smaller
    SDL_FreeYUVOverlay(vp->bmp);
  }
  // Allocate a place to put our YUV image on that screen
  SDL_LockMutex(screen_mutex);
  vp->bmp = SDL_CreateYUVOverlay(is->video_ctx->width,
				 is->video_ctx->height,
				 SDL_YV12_OVERLAY,
				 screen);
  SDL_UnlockMutex(screen_mutex);

  vp->width = is->video_ctx->width;
  vp->height = is->video_ctx->height;
  vp->allocated = 1;

}

int queue_picture(VideoState *is, AVFrame *pFrame, double pts) {

  VideoPicture *vp;
  int dst_pix_fmt;
  AVPicture pict;

  /* wait until we have space for a new pic */
  SDL_LockMutex(is->pictq_mutex);
  while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
	!is->quit) {
    SDL_CondWait(is->pictq_cond, is->pictq_mutex);
  }
  SDL_UnlockMutex(is->pictq_mutex);

  if(is->quit)
    return -1;

  // windex is set to 0 initially
  vp = &is->pictq[is->pictq_windex];

  /* allocate or resize the buffer! */
  if(!vp->bmp ||
     vp->width != is->video_ctx->width ||
     vp->height != is->video_ctx->height) {
    SDL_Event event;

    vp->allocated = 0;
    alloc_picture(is);
    if(is->quit) {
      return -1;
    }
  }
  /* We have a place to put our picture on the queue */

  if(vp->bmp) {

    SDL_LockYUVOverlay(vp->bmp);
    vp->pts = pts;
    
    dst_pix_fmt = PIX_FMT_YUV420P;
    /* point pict at the queue */

    pict.data[0] = vp->bmp->pixels[0];
    pict.data[1] = vp->bmp->pixels[2];
    pict.data[2] = vp->bmp->pixels[1];
    
    pict.linesize[0] = vp->bmp->pitches[0];
    pict.linesize[1] = vp->bmp->pitches[2];
    pict.linesize[2] = vp->bmp->pitches[1];
    
    // Convert the image into YUV format that SDL uses
    sws_scale(is->sws_ctx, (uint8_t const * const *)pFrame->data,
	      pFrame->linesize, 0, is->video_ctx->height,
	      pict.data, pict.linesize);
    
    SDL_UnlockYUVOverlay(vp->bmp);
    /* now we inform our display thread that we have a pic ready */
    if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) {
      is->pictq_windex = 0;
    }
    SDL_LockMutex(is->pictq_mutex);
    is->pictq_size++;
    SDL_UnlockMutex(is->pictq_mutex);
  }
  return 0;
}

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_ctx->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;
}

int video_thread(void *arg) {
  VideoState *is = (VideoState *)arg;
  AVPacket pkt1, *packet = &pkt1;
  int frameFinished;
  AVFrame *pFrame;
  double pts;

  pFrame = av_frame_alloc();

  for(;;) {
    if(packet_queue_get(&is->videoq, packet, 1) < 0) {
      // means we quit getting packets
      break;
    }
    if(packet_queue_get(&is->videoq, packet, 1) < 0) {
      // means we quit getting packets
      break;
    }
    pts = 0;

    // Decode video frame
    avcodec_decode_video2(is->video_ctx, pFrame, &frameFinished, packet);

    if((pts = av_frame_get_best_effort_timestamp(pFrame)) == AV_NOPTS_VALUE) {
      pts = av_frame_get_best_effort_timestamp(pFrame);
    } else {
      pts = 0;
    }
    pts *= av_q2d(is->video_st->time_base);

    // Did we get a video frame?
    if(frameFinished) {
      pts = synchronize_video(is, pFrame, pts);
      if(queue_picture(is, pFrame, pts) < 0) {
	break;
      }
    }
    av_free_packet(packet);
  }
  av_frame_free(&pFrame);
  return 0;
}

int stream_component_open(VideoState *is, int stream_index) {

  AVFormatContext *pFormatCtx = is->pFormatCtx;
  AVCodecContext *codecCtx = NULL;
  AVCodec *codec = NULL;
  SDL_AudioSpec wanted_spec, spec;

  if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) {
    return -1;
  }

  codec = avcodec_find_decoder(pFormatCtx->streams[stream_index]->codec->codec_id);
  if(!codec) {
    fprintf(stderr, "Unsupported codec!\n");
    return -1;
  }

  codecCtx = avcodec_alloc_context3(codec);
  if(avcodec_copy_context(codecCtx, pFormatCtx->streams[stream_index]->codec) != 0) {
    fprintf(stderr, "Couldn't copy codec context");
    return -1; // Error copying codec context
  }


  if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {
    // Set audio settings from codec info
    wanted_spec.freq = codecCtx->sample_rate;
    wanted_spec.format = AUDIO_S16SYS;
    wanted_spec.channels = codecCtx->channels;
    wanted_spec.silence = 0;
    wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
    wanted_spec.callback = audio_callback;
    wanted_spec.userdata = is;
    
    if(SDL_OpenAudio(&wanted_spec, &spec) < 0) {
      fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
      return -1;
    }
    is->audio_hw_buf_size = spec.size;
  }
  if(avcodec_open2(codecCtx, codec, NULL) < 0) {
    fprintf(stderr, "Unsupported codec!\n");
    return -1;
  }

  switch(codecCtx->codec_type) {
  case AVMEDIA_TYPE_AUDIO:
    is->audioStream = stream_index;
    is->audio_st = pFormatCtx->streams[stream_index];
    is->audio_ctx = codecCtx;
    is->audio_buf_size = 0;
    is->audio_buf_index = 0;
    memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
    packet_queue_init(&is->audioq);
    SDL_PauseAudio(0);
    break;
  case AVMEDIA_TYPE_VIDEO:
    is->videoStream = stream_index;
    is->video_st = pFormatCtx->streams[stream_index];
    is->video_ctx = codecCtx;

    is->frame_timer = (double)av_gettime() / 1000000.0;
    is->frame_last_delay = 40e-3;
    is->video_current_pts_time = av_gettime();

    packet_queue_init(&is->videoq);
    is->video_tid = SDL_CreateThread(video_thread, is);
    is->sws_ctx = sws_getContext(is->video_ctx->width, is->video_ctx->height,
				 is->video_ctx->pix_fmt, is->video_ctx->width,
				 is->video_ctx->height, PIX_FMT_YUV420P,
				 SWS_BILINEAR, NULL, NULL, NULL
				 );
    break;
  default:
    break;
  }
}

int decode_thread(void *arg) {

  VideoState *is = (VideoState *)arg;
  AVFormatContext *pFormatCtx;
  AVPacket pkt1, *packet = &pkt1;

  int video_index = -1;
  int audio_index = -1;
  int i;

  is->videoStream=-1;
  is->audioStream=-1;

  global_video_state = is;

  // Open video file
  if(avformat_open_input(&pFormatCtx, is->filename, NULL, NULL)!=0)
    return -1; // Couldn't open file

  is->pFormatCtx = pFormatCtx;
  
  // Retrieve stream information
  if(avformat_find_stream_info(pFormatCtx, NULL)<0)
    return -1; // Couldn't find stream information
  
  // Dump information about file onto standard error
  av_dump_format(pFormatCtx, 0, is->filename, 0);
  
  // Find the first video stream

  for(i=0; i<pFormatCtx->nb_streams; i++) {
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO &&
       video_index < 0) {
      video_index=i;
    }
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
       audio_index < 0) {
      audio_index=i;
    }
  }
  if(audio_index >= 0) {
    stream_component_open(is, audio_index);
  }
  if(video_index >= 0) {
    stream_component_open(is, video_index);
  }   

  if(is->videoStream < 0 || is->audioStream < 0) {
    fprintf(stderr, "%s: could not open codecs\n", is->filename);
    goto fail;
  }

  // main decode loop

  for(;;) {
    if(is->quit) {
      break;
    }
    // seek stuff goes here
    if(is->seek_req) {
      int stream_index= -1;
      int64_t seek_target = is->seek_pos;

      if     (is->videoStream >= 0) stream_index = is->videoStream;
      else if(is->audioStream >= 0) stream_index = is->audioStream;

      if(stream_index>=0){
	seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q,
				  pFormatCtx->streams[stream_index]->time_base);
      }
      if(av_seek_frame(is->pFormatCtx, stream_index, 
		       seek_target, is->seek_flags) < 0) {
	fprintf(stderr, "%s: error while seeking\n",
		is->pFormatCtx->filename);
      } else {

	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);
	}
      }
      is->seek_req = 0;
    }

    if(is->audioq.size > MAX_AUDIOQ_SIZE ||
       is->videoq.size > MAX_VIDEOQ_SIZE) {
      SDL_Delay(10);
      continue;
    }
    if(av_read_frame(is->pFormatCtx, packet) < 0) {
      if(is->pFormatCtx->pb->error == 0) {
	SDL_Delay(100); /* no error; wait for user input */
	continue;
      } else {
	break;
      }
    }
    // Is this a packet from the video stream?
    if(packet->stream_index == is->videoStream) {
      packet_queue_put(&is->videoq, packet);
    } else if(packet->stream_index == is->audioStream) {
      packet_queue_put(&is->audioq, packet);
    } else {
      av_free_packet(packet);
    }
  }
  /* all done - wait for it */
  while(!is->quit) {
    SDL_Delay(100);
  }

 fail:
  if(1){
    SDL_Event event;
    event.type = FF_QUIT_EVENT;
    event.user.data1 = is;
    SDL_PushEvent(&event);
  }
  return 0;
}

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;
  }
}

int main(int argc, char *argv[]) {

  SDL_Event       event;

  VideoState      *is;

  is = av_mallocz(sizeof(VideoState));

  if(argc < 2) {
    fprintf(stderr, "Usage: test <file>\n");
    exit(1);
  }
  // Register all formats and codecs
  av_register_all();
  
  if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
    fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
    exit(1);
  }

  // Make a screen to put our video
#ifndef __DARWIN__
        screen = SDL_SetVideoMode(640, 480, 0, 0);
#else
        screen = SDL_SetVideoMode(640, 480, 24, 0);
#endif
  if(!screen) {
    fprintf(stderr, "SDL: could not set video mode - exiting\n");
    exit(1);
  }

  screen_mutex = SDL_CreateMutex();

  av_strlcpy(is->filename, argv[1], sizeof(is->filename));

  is->pictq_mutex = SDL_CreateMutex();
  is->pictq_cond = SDL_CreateCond();

  schedule_refresh(is, 40);

  is->av_sync_type = DEFAULT_AV_SYNC_TYPE;
  is->parse_tid = SDL_CreateThread(decode_thread, is);
  if(!is->parse_tid) {
    av_free(is);
    return -1;
  }

  av_init_packet(&flush_pkt);
  flush_pkt.data = "FLUSH";

  for(;;) {
    double incr, pos;
    SDL_WaitEvent(&event);
    switch(event.type) {
    case SDL_KEYDOWN:
      switch(event.key.keysym.sym) {
      case SDLK_LEFT:
	incr = -10.0;
	goto do_seek;
      case SDLK_RIGHT:
	incr = 10.0;
	goto do_seek;
      case SDLK_UP:
	incr = 60.0;
	goto do_seek;
      case SDLK_DOWN:
	incr = -60.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;
      default:
	break;
      }
      break;
    case FF_QUIT_EVENT:
    case SDL_QUIT:
      is->quit = 1;
      /*
       * If the video has finished playing, then both the picture and
       * audio queues are waiting for more data.  Make them stop
       * waiting and terminate normally.
       */
      SDL_CondSignal(is->audioq.cond);
      SDL_CondSignal(is->videoq.cond);
      SDL_Quit();
      return 0;
      break;
    case FF_REFRESH_EVENT:
      video_refresh_timer(event.user.data1);
      break;
    default:
      break;
    }
  }
  return 0;

}

Processamento de comandos de pesquisa


Agora vamos adicionar alguns recursos de pesquisa ao nosso player, porque é realmente irritante quando você não pode retroceder o filme. Além disso, veremos como é fácil usar a função av_seek_frame .

Vamos fazer as setas do teclado "esquerda" e "direita" rolar um pouco o filme para frente e para trás, e as setas "para cima" e "para baixo" já são mais significativas. "Um pouco" - serão 10 segundos e "muito" - todos os 60. Portanto, precisamos configurar nosso loop principal para que ele intercepte eventos de pressionamento de tecla. Mas o fato é que, quando pressionamos uma tecla, não podemos chamar av_seek_frame diretamente. Isso deve ser feito em nosso loop de decodificação principal, decode_thread loop. Portanto, em vez disso, adicionaremos alguns valores à estrutura principal, que conterão uma nova posição para a pesquisa e alguns sinalizadores de pesquisa:

  int             seek_req;
  int             seek_flags;
  int64_t         seek_pos;

Agora precisamos configurar nosso loop principal que captura as teclas:

  for(;;) {
    double incr, pos;

    SDL_WaitEvent(&event);
    switch(event.type) {
    case SDL_KEYDOWN:
      switch(event.key.keysym.sym) {
      case SDLK_LEFT:
	incr = -10.0;
	goto do_seek;
      case SDLK_RIGHT:
	incr = 10.0;
	goto do_seek;
      case SDLK_UP:
	incr = 60.0;
	goto do_seek;
      case SDLK_DOWN:
	incr = -60.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;
      default:
	break;
      }
      break;

Para capturar um pressionamento de tecla, primeiro examinamos se o evento SDL_KEYDOWN ocorreu . Em seguida, verificamos qual chave é recebida usando event.key.keysym.sym . Assim que descobrimos em que direção estamos olhando, calculamos um novo tempo, adicionando um incremento ao valor da nossa nova função get_master_clock . Em seguida, chamamos a função stream_seek para definir os valores de search_pos etc. Converta nosso novo horário em unidades de carimbo de data / hora internas da avcodec . Lembre-se de que os registros de data e hora nos fluxos são medidos em quadros, não segundos, usando a seguinte fórmula: segundos = quadros * time_base ( fps ).Por padrão, o avcodec é definido como 1.000.000 de quadros por segundo (portanto, uma posição de 2 segundos terá um carimbo de data / hora de 2.000.000). Por que precisamos converter esse valor - veja mais adiante.

Aqui está nossa função stream_seek . Observe que definiremos o sinalizador se voltarmos:

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;
  }
}

Agora vamos para o nosso decode_thread , onde realizamos uma pesquisa. Nos arquivos de origem, você pode ver que marcamos a área "a pesquisa está em andamento". Bem, vamos colocá-lo lá agora.

A pesquisa é centrada na função av_seek_frame . Essa função usa como argumento o contexto de formato, fluxo, carimbo de data e hora e conjunto de sinalizadores. A função procurará o carimbo de data / hora especificado. A unidade do registro de data e hora é a time_base do fluxo que você passa para a função. No entanto, você não precisa transmiti-lo ao fluxo (indicado pela passagem do valor -1). Se você fizer isso, o time_base estará na unidade de tempo interna da avcodecou 1000000fps. É por isso que multiplicamos nossa posição por AV_TIME_BASE quando definimos o search_pos .

No entanto, às vezes você pode (raramente) encontrar problemas para alguns arquivos se você passar av_seek_frame - 1 para o fluxo, portanto, vamos selecionar o primeiro fluxo em nosso arquivo e passá-lo para av_seek_frame . Não esqueça que devemos alterar a escala do nosso carimbo de data e hora para estar no novo "sistema de coordenadas".

if(is->seek_req) {
  int stream_index= -1;
  int64_t seek_target = is->seek_pos;

  if     (is->videoStream >= 0) stream_index = is->videoStream;
  else if(is->audioStream >= 0) stream_index = is->audioStream;

  if(stream_index>=0){
    seek_target= av_rescale_q(seek_target, AV_TIME_BASE_Q,
                      pFormatCtx->streams[stream_index]->time_base);
  }
  if(av_seek_frame(is->pFormatCtx, stream_index, 
                    seek_target, is->seek_flags) < 0) {
    fprintf(stderr, "%s: error while seeking\n",
            is->pFormatCtx->filename);
  } else {
     /* handle packet queues... more later... */

av_rescale_q ( a , b , c ) é uma função que escala o registro de data e hora de uma base para outra. Ele basicamente calcula a * b / c , mas essa função é útil porque esse cálculo às vezes leva ao estouro. AV_TIME_BASE_Q é uma versão fracionária de AV_TIME_BASE . Eles são completamente diferentes: AV_TIME_BASE * time_in_seconds = avcodec_timestamp e AV_TIME_BASE_Q * avcodec_timestamp = time_in_seconds (mas observe que AV_TIME_BASE_Qé realmente um objeto AVRational , então você precisa usar funções q especiais no avcodec para processá-lo).

Limpeza de Buffer


Portanto, configuramos nossas pesquisas corretamente, mas ainda não terminamos. Lembre-se, temos uma fila configurada para acumular pacotes? Agora que estamos com um carimbo de data e hora diferente, precisamos limpar essa fila, caso contrário, a pesquisa no filme não funcionará! Além disso, o avcodec possui seus próprios buffers internos, que também precisam ser liberados para cada fluxo.

Para fazer isso, você deve primeiro escrever uma função que limpe nossa fila de pacotes. Então você precisa de alguma forma instruir o fluxo de áudio e vídeo que eles limparam os buffers internos do avcodec . Podemos fazer isso colocando um pacote especial na fila após a limpeza, e quando eles (threads) descobrem esse pacote especial, eles simplesmente limpam seus buffers.

Vamos começar com a função de redefinição. É realmente muito simples, então vou mostrar o código:

static void packet_queue_flush(PacketQueue *q) {
  AVPacketList *pkt, *pkt1;

  SDL_LockMutex(q->mutex);
  for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) {
    pkt1 = pkt->next;
    av_free_packet(&pkt->pkt);
    av_freep(&pkt);
  }
  q->last_pkt = NULL;
  q->first_pkt = NULL;
  q->nb_packets = 0;
  q->size = 0;
  SDL_UnlockMutex(q->mutex);
}

Agora que a fila está limpa, adicione nosso "pacote de limpeza". Mas primeiro, seria bom definir o que é e criá-lo:

AVPacket flush_pkt;

main() {
  ...
  av_init_packet(&flush_pkt);
  flush_pkt.data = "FLUSH";
  ...
}

Agora coloque este pacote na fila:

  } else {
    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);
    }
  }
  is->seek_req = 0;
}

(Esse trecho de código continua o trecho de código acima para decode_thread .) Também precisamos modificar packet_queue_put para não duplicar um pacote especial para limpeza:

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

  AVPacketList *pkt1;
  if(pkt != &flush_pkt && av_dup_packet(pkt) < 0) {
    return -1;
  }

E então, no fluxo de áudio e vídeo, colocamos essa chamada em avcodec_flush_buffers logo após packet_queue_get :

    if(packet_queue_get(&is->audioq, pkt, 1) < 0) {
      return -1;
    }
    if(pkt->data == flush_pkt.data) {
      avcodec_flush_buffers(is->audio_st->codec);
      continue;
    }

O trecho de código acima é exatamente o mesmo para o fluxo de vídeo, com a substituição de "áudio" por "vídeo".

É isso! Conseguimos! Compile seu player:

gcc -o tutorial07 tutorial07.c -lavutil -lavformat -lavcodec -lswscale -lz -lm \
`sdl-config --cflags --libs`

e aproveite o seu reprodutor de filmes feito em menos de 1000 linhas de C!

Embora, é claro, existam muitas coisas que podem ser adicionadas ou aprimoradas.






Posfácio


Então, temos um jogador que trabalha, mas é claro que não é tão bom quanto poderia ser. Seria possível modificar o arquivo e adicionar muitas coisas úteis:

  • Vamos ser sinceros, esse jogador é péssimo. A versão do ffplay.c na qual ela se baseia está completamente desatualizada e, como resultado, este tutorial precisa ser completamente revisado. Se você deseja avançar para projetos mais sérios usando as bibliotecas FFmpeg, recomendo fortemente que você verifique a versão mais recente do ffplay.c como a próxima tarefa.
  • A manipulação de erros em nosso código é terrível e pode ser implementada muito melhor.
  • , , , . , paused , , . , , . av_read_play. - , . , , . : , ffplay.c.
  • .
  • . , , , , VOB-.
  • . , .
  • . .
  • , , , , YUV, time_base.
  • .
  • --, ; ffplay.c .


Se você quiser saber mais sobre o FFmpeg, aqui consideramos longe de tudo. O próximo passo é estudar a codificação multimídia. É melhor começar com o arquivo output_example.c , que você encontrará na distribuição FFmpeg. Eu poderia escrever outro livro sobre esse assunto, mas é improvável que supere este guia.

UPD.Há muito tempo, não atualizei este texto, mas, enquanto isso, o mundo não pára. Este tutorial requer apenas atualizações simples da API; muito pouco mudou em termos de conceitos básicos. A maioria dessas atualizações simplificou o código. No entanto, embora eu tenha percorrido todo o código e o atualizado, o FFplay ainda é superior a este reprodutor de brinquedos. De coração, admitimos: nessas lições, escrevemos um péssimo reprodutor de filmes. Portanto, se hoje (ou no futuro) você deseja melhorar este tutorial, recomendo que você se familiarize com o FFplay e descubra o que está faltando. Acredito que isso se refira principalmente ao uso de equipamento de vídeo, mas, possivelmente, estou perdendo outras coisas óbvias. Talvez uma comparação com o FFplay atual leve a uma reescrita radical de algumas coisas - eu ainda não o assisti.

Mas estou muito orgulhoso de que, ao longo dos anos, meu trabalho tenha ajudado muito, mesmo levando em conta o fato de que as pessoas geralmente procuravam código em outro lugar. Sou imensamente grato a Chelyaev , que assumiu a rotina de substituir todas as funções desatualizadas desde que escrevi essa monografia há 8 (!) Anos.

Alegro-me na esperança de que essas lições tenham sido úteis e não chatas. Se houver sugestões, erros, reclamações, agradecimentos etc. em relação a este guia, escreva-me em dranger dog gmail dot com. E sim, não faz sentido pedir que eu ajude no seu projeto FFmpeg. também muitas cartas semelhantes .






Apêndice 1. Lista de funções


int avformat_open_input(AVFormatContext **ptr, const char * filename, AVInputFormat *fmt, AVDictionary **options)

Abre o nome do arquivo de mídia, salva o contexto do formato no endereço especificado em ptr .

fmt : se não for NULL, define o formato do arquivo.
buf_size : tamanho do buffer (opcional).
opções : AVDictionary é preenchido com os parâmetros do AVFormatContext e desmultiplexador.

void avformat_close_input(AVFormatContext **s)

Fecha o arquivo de mídia. No entanto, ele não fecha os codecs.

nt avio_open2 (AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options)

Cria um contexto de E / S para usar o recurso especificado na URL .

s : ponteiro para o local onde o AVIOContext será criado . Em caso de falha, o valor especificado é definido como NULL.
url : nome do recurso a ser acessado.
flags : controla a abertura do recurso especificado no URL .
int_cb : interrompe o retorno de chamada para uso no nível do protocolo.
opções : um dicionário preenchido com parâmetros de protocolo privado. Quando a função retornar, o parâmetro será destruído e substituído por um ditado contendo opções que não foram encontradas. Pode ser NULL.

int av_dup_packet(AVPacket *pkt)

Obviamente, isso é um hack: se este pacote não foi alocado, nós o publicamos aqui. Retorna 0 em caso de sucesso ou AVERROR_NOMEM em falha.

int av_find_stream_info(AVFormatContext *s, AVDictionary **options)

Essa função procura informações de fluxo não óbvias, como taxa de quadros. Isso é útil para formatos de arquivo sem cabeçalho, como MPEG. É recomendável ligar depois de abrir o arquivo. Retorna> = 0 se for bem-sucedido, AVERROR_ * em caso de erro.

AVFrame *avcodec_free_frame()

Nome antigo para av_frame_free. Alterado em lavc 55.28.1.

void av_frame_free (AVFrame **frame)

Libera um quadro e quaisquer objetos alocados dinamicamente nele, por exemplo, extended_data.

void av_free(void *ptr)

Libera memória alocada usando av_malloc () ou av_realloc (). Você pode chamar esta função com ptr == NULL. É recomendável que você chame av_freep ().

void av_freep(void *ptr)

Libera memória e define o ponteiro para NULL. Usa internamente av_free ().

void av_free_packet(AVPacket *pkt)

Envolva o método de destruição de pacotes (pkt-> destruct).

int64_t av_gettime()

Obtenha a hora atual em microssegundos.

void av_init_packet(AVPacket *pkt)

Inicializando campos de pacote opcionais.

void *av_malloc(unsigned int size)

Tamanho de bytes de alocação de memória com alinhamento adequado para todos os acessos à memória (incluindo vetores, se disponíveis na CPU). av_malloc (0) deve retornar um ponteiro diferente de zero.

void *av_mallocz(unsigned int size)

O mesmo que av_malloc (), mas inicializa a memória para zero.

double av_q2d(AVRational a)

Duplas AVRational.

int av_read_frame(AVFormatContext *s, AVPacket *pkt)

Retorna o próximo quadro de fluxo. As informações são armazenadas como um pacote na pkt.

O pacote retornado é válido até o próximo av_read_frame () ou até av_close_input_file () e deve ser liberado usando av_free_packet. Para um pacote de vídeo, contém exatamente um quadro. Para áudio, ele contém um número inteiro de quadros se cada quadro tiver um tamanho fixo conhecido (por exemplo, dados PCM ou ADPCM). Se os quadros de áudio tiverem tamanho variável (por exemplo, áudio MPEG), ele conterá um quadro.

pkt-> pts, pkt-> dts e pkt-> duration são sempre definidos com os valores corretos em unidades de AVStream.timebase (e supõe-se que o formato não possa fornecê-los). pkt-> pts pode ser AV_NOPTS_VALUE se o formato de vídeo tiver quadros B, por isso é melhor confiar em pkt-> dts se você não estiver descompactando a carga útil.

Resultado retornado: 0, se estiver tudo bem, <0, se houver um erro ou o final do arquivo.

void av_register_all();

Registra todos os codecs na biblioteca.

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)

Retorna a * bq / cq .

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)

Procura um quadro-chave em um carimbo de data / hora.

stream_index : se stream_index for -1, o fluxo padrão será selecionado e o registro de data e hora será automaticamente convertido de unidades AV_TIME_BASE em um time_base específico do fluxo.
timestamp : timestamp medido em unidades de AVStream.time_base ou, se nenhum fluxo for especificado, em unidades de AV_TIME_BASE.
sinalizadores : defina os parâmetros relativos à direção e modo de busca:
AVSEEK_FLAG_ANY: procure em qualquer quadro, não apenas nos principais.
AVSEEK_FLAG_BACKWARD: pesquise na direção oposta.
AVSEEK_FLAG_BYTE: pesquisa com base na posição em bytes.

AVFrame *avcodec_alloc_frame()

Nome antigo para av_frame_alloc. Alterado em lavc 55.28.1.

AVFrame *av_frame_alloc()

Seleciona um AVFrame e o inicializa. Pode ser liberado usando av_frame_free ().

int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, const AVPacket *avpkt)

Decodifica um quadro de áudio de avpkt para quadro. A função avcodec_decode_audio4 () decodifica um arquivo de áudio do AVPacket. Para sua decodificação, é usado um codec de áudio, que foi associado ao avctx usando avcodec_open2 (). O quadro decodificado resultante é armazenado no AVFrame especificado. Se o quadro tiver sido descompactado, ele definirá got_frame_ptr como 1.

Aviso: o buffer de entrada, avpkt-> data, deve ser FF_INPUT_BUFFER_PADDING_SIZE maior que os bytes de leitura reais, porque alguns leitores de fluxo de bits otimizados leem 32 ou 64 bits por vez e podem ler até o fim.

avctx : contexto do codec.
quadro : quadro de destino.
got_frame_ptr : target int, que será definido se o quadro for descompactado.
AVPKT: AVPacket contendo áudio.

Resultado retornado: se um erro for retornado, um valor negativo será retornado; caso contrário, o número de bytes usados ​​na entrada AVPacket será retornado.

int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *frameFinished, const AVPacket *avpkt)

Decodifica um quadro de vídeo de buf para uma imagem. A função avcodec_decode_video2 () decodifica um quadro de vídeo a partir de um buffer de entrada do tamanho buf_size. Para sua decodificação, é usado um codec de vídeo associado ao avctx usando avcodec_open2 (). O quadro decodificado resultante é salvo na imagem.

Aviso: exemplos de alinhamento e problemas de buffer que se aplicam a avcodec_decode_audio4 também se aplicam a esta função.

avctx : contexto do codec.
picture : AVFrame no qual o vídeo decodificado será salvo.
frameFinished : zero se nenhum quadro puder ser descompactado, caso contrário, não será igual a zero.
avpkt: entrada AVPacket contendo buffer de entrada. Você pode criar esse pacote usando av_init_packet () e, com dados e tamanho especificados, alguns decodificadores podem precisar adicionalmente de outros campos, como sinalizadores e AV_PKT_FLAG_KEY. Todos os decodificadores são projetados para usar o menor número possível de campos.

Resultado retornado: Em caso de erro, um valor negativo é retornado; caso contrário, o número de bytes é usado ou zero se nenhum quadro puder ser descompactado.

int64_t av_frame_get_best_effort_timestamp (const AVFrame *frame)

Um método de acesso simples para obter best_effort_timestamp a partir de um objeto AVFrame.

AVCodec *avcodec_find_decoder(enum CodecID id)

Procura por um decodificador com CodecID. Retorna NULL em erro. Ele deve ser chamado depois de obter o AVCodecContext necessário a partir do fluxo no AVFormatContext usando codecCtx-> codec_id.

void avcodec_flush_buffers(AVCodecContetx *avctx)

Tampão nivelado. Chamado ao pesquisar ou alternar para outro fluxo.

AVCodecContext * avcodec_alloc_context3 (const AVCodec *codec)

Atribui AVCodecContext e define seus campos para os valores padrão.

int avcodec_copy_context (AVCodecContext *dest, const AVCodecContext *src)

Copie as configurações do AVCodecContext de origem no AVCodecContext de destino. O contexto resultante do codec de destino será fechado, ou seja, você deve chamar avcodec_open2 () antes de usar este AVCodecContext para decodificar / codificar dados de vídeo / áudio.

dest : deve ser inicializado com avcodec_alloc_context3 (NULL), caso contrário, não será inicializado.

int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)

Inicializa o avctx para usar o codec especificado no codec . Deve ser usado após avcodec_find_decoder. Retorna zero em caso de sucesso e um valor negativo em caso de erro.

int avpicture_fill(AVPicture *picture, uint8_t *ptr, int pix_fmt, int width, int height)

Define a estrutura para a qual a imagem aponta, com o buffer ptr , o formato pix_fmt e a largura e altura especificadas. Retorna o tamanho dos dados da imagem em bytes.

int avpicture_get_size(int pix_fmt, int width, int height)

Calcula quantos bytes são necessários para uma imagem de uma determinada largura, altura e formato de imagem.

struct SwsContext* sws_getContext(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, double *param)

Retorna SwsContext para uso em sws_scale.

srcW , srcH , srcFormat : largura, altura e formato dos pixels desejados.
dstW , dstH , dstFormat : largura, altura e formato dos pixels finais.
sinalizadores : o método de dimensionamento a ser usado.
As seguintes opções estão disponíveis: SWS_FAST_BILINEAR, SWS_BILINEAR, SWS_BICUBIC, SWS_X, SWS_POINT, SWS_AREA, SWS_BICUBLIN, SWS_GAUSS, SWS_SINC, SWS_LANCZOS, SWS_SPLINE.
Outros sinalizadores incluem sinalizadores de capacidade da CPU: SWS_CPU_CAPS_MMX, SWS_CPU_CAPS_MMX2, SWS_CPU_CAPS_3DNOW, SWS_CPU_CAPS_ALTIVEC.
Outros sinalizadores incluem (atualmente não totalmente implementado) SWS_FULL_CHR_H_INT, SWS_FULL_CHR_H_INP e SWS_DIRECT_BGR.
Por fim, há SWS_ACCURATE_RND e talvez o mais útil para iniciantes, SWS_PRINT_INFO.
Não tenho ideia do que a maioria deles faz. Talvez escreva para mim?
srcFilter , dstFilter : SwsFilter para origem e destino. O SwsFilter permite a filtragem de cores / brilho. O valor padrão é NULL.
param : deve ser um ponteiro para um buffer int [2] com coeficientes. Não documentado. Parece ser usado para modificar levemente os algoritmos de escala padrão. O valor padrão é NULL. Somente para especialistas!

int sws_scale(SwsContext *c, uint8_t *src, int srcStride[], int srcSliceY, int srcSliceH, uint8_t dst[], int dstStride[]
sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, is->video_st->codec->height, pict.data, pict.linesize);

Dimensiona os dados no src de acordo com nossas configurações em nosso SwsContext * c .
srcStride e dstStride são os tamanhos das linhas de origem e de destino.

SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)

Adiciona uma função de retorno de chamada que é executada após um número especificado de milissegundos. A função de retorno de chamada passa o intervalo atual do timer e o parâmetro fornecido pelo usuário da chamada SDL_AddTimer e retorna o próximo intervalo do timer. (Se o valor de retorno do retorno de chamada corresponder ao valor passado, o cronômetro continuará funcionando na mesma velocidade.) Se o valor de retorno do retorno de chamada for 0, o cronômetro será cancelado.
Outra maneira de cancelar o timer atual é chamar SDL_RemoveTimer com o identificador de timer (retornado do SDL_AddTimer).

A função de retorno de chamada do timer pode ser executada em um segmento diferente do seu programa principal e, portanto, não deve chamar nenhuma função por si mesma. No entanto, você sempre pode chamar SDL_PushEvent.

O grau de detalhe do cronômetro depende da plataforma, mas você deve esperar pelo menos 10 ms, pois esse é o valor mais comum. Isso significa que, se você solicitar um timer de 16 ms, o retorno de chamada será iniciado após cerca de 20 ms em um sistema descarregado. Se você precisar definir um sinalizador que sinalize a atualização de quadros a uma velocidade de 30 quadros por segundo (a cada 33 ms), poderá definir um temporizador para 30 ms (veja o exemplo abaixo). Se você usar esta função, precisará passar SDL_INIT_TIMER para SDL_Init.

Retorna o valor do identificador para o timer adicionado, ou NULL se ocorrer um erro.

Formato para retorno de chamada:
Uint32 callback ( Uint32, void * param)


int SDL_CondSignal(SDL_cond *cond)

Reiniciando um dos encadeamentos aguardando a variável de condição cond . Retorna 0 em caso de sucesso e -1 em caso de erro.

int SDL_CondWait(SDL_cond *cond, SDL_mutex *mut);

Desbloqueie o mutex fornecido e aguarde outro segmento chamar SDL_CondSignal ou SDL_CondBroadcast para a variável de condição cond e, em seguida, bloqueie novamente o mutex. O mutex deve ser bloqueado antes de entrar nesta função. Retorna 0 quando um sinal é recebido ou -1 em caso de erro.

SDL_cond *SDL_CreateCond(void);

Cria uma variável de condição.

SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data);

SDL_CreateThread cria um novo encadeamento de execução que compartilha toda a memória global de seu pai, manipuladores de sinal, descritores de arquivo etc. E executa a função fn , passando os dados do ponteiro nulo. O encadeamento termina quando fn retorna um valor.

void SDL_Delay (Uint32 );

Aguarda o número especificado de milissegundos. O SDL_Delay aguardará pelo menos o tempo especificado, mas possivelmente mais por causa do planejamento do SO.
Nota: espere uma granularidade de atraso de pelo menos 10 ms. Algumas plataformas têm medidas mais curtas, mas esta é a opção mais comum.

SDL_Overlay *SDL_CreateYUVOverlay(int width, int height, Uint32 format, SDL_Surface *display);

SDL_CreateYUVOverlay cria uma sobreposição YUV da largura, altura e formato especificados (para obter uma lista dos formatos disponíveis, consulte a estrutura de dados SDL_Overlay) para a exibição fornecida. Retorna SDL_Overlay.

display deve ser uma superfície derivada de SDL_SetVideoMode, caso contrário, essa função funcionará por padrão.

O termo “sobreposição” está incorreto, porque se a sobreposição não for criada no hardware, o conteúdo da superfície de exibição abaixo da área em que a sobreposição é exibida será sobrescrito quando a sobreposição for exibida.

int SDL_LockYUVOverlay(SDL_Overlay *overlay)

SDL_LockYUVOverlay bloqueia a sobreposição para acesso direto aos dados de pixel. Retorna 0 em caso de sucesso ou -1 em caso de erro.

void SDL_UnlockYUVOverlay(SDL_Overlay *overlay)

Desbloqueia uma sobreposição bloqueada anteriormente. A sobreposição deve ser desbloqueada antes de poder ser exibida.

int SDL_DisplayYUVOverlay(SDL_Overlay *overlay, SDL_Rect *dstrect)

Coloca a sobreposição na superfície especificada quando foi criada. A estrutura SDL_Rect dstrect define a posição e o tamanho do destino. Se o dstrect tiver mais ou menos sobreposição, a sobreposição será dimensionada; isso é otimizado para 2x. Retorna 0 se for bem-sucedido.

void SDL_FreeYUVOverlay(SDL_Overlay *overlay)

Libera a sobreposição criada por SDL_CreateYUVOverlay.

int SDL_Init(Uint32 flags);

Inicializa o SDL. Isso deve ser chamado antes de todas as outras funções SDL. O parâmetro flags especifica quais partes do SDL inicializar.

SDL_INIT_TIMER - inicializa o subsistema de timer.
SDL_INIT_AUDIO - inicializa o subsistema de áudio.
SDL_INIT_VIDEO - inicializa o subsistema de vídeo.
SDL_INIT_CDROM - inicializa o subsistema de CD-ROM.
SDL_INIT_JOYSTICK - inicializa o subsistema do joystick.
SDL_INIT_EVERYTHING - Inicializa todas as opções acima.
SDL_INIT_NOPARACHUTE - não permite que o SDL capture erros fatais.
SDL_INIT_EVENTTHREAD - inicia o gerenciador de eventos em um encadeamento separado.

Retorna -1 com erro ou 0 com êxito. Você pode receber uma mensagem de erro estendida chamando SDL_GetError. Uma causa típica de um erro é o uso de uma exibição específica sem o suporte correspondente para o subsistema, por exemplo, a ausência de um driver de mouse ao usar um buffer de quadro com o dispositivo. Nesse caso, você pode compilar o SDL sem um mouse ou definir a variável de ambiente "SDL_NOMOUSE = 1" antes de iniciar o aplicativo.

SDL_mutex *SDL_CreateMutex(void);

Cria um novo mutex desbloqueado.

int SDL_LockMutex(SDL_mutex *mutex)

SDL_LockMutex é um alias para SDL_mutexP. Ele bloqueia um mutex que foi criado anteriormente usando SDL_CreateMutex. Se o mutex já estiver bloqueado por outro encadeamento, o SDL_mutexP não retornará um valor até que o encadeamento bloqueado por ele o desbloqueie (usando SDL_mutexV). Quando o mutex é chamado novamente, SDL_mutexV (também conhecido como SDL_UnlockMutex) deve ser chamado um número igual de vezes para retornar o mutex a um estado desbloqueado. Retorna 0 em caso de sucesso ou -1 em caso de erro.

int SDL_UnlockMutex(SDL_Mutex *mutex)

Desbloqueio Mutex.

int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)

Esta função abre a unidade de áudio com os parâmetros necessários e retorna 0 se for bem-sucedida, colocando os parâmetros de hardware reais na estrutura para a qual finalmente aponta. Se um valor NULL for recebido, os dados de áudio passados ​​para a função de retorno de chamada terão o formato necessário e, se necessário, serão convertidos automaticamente para o formato de áudio do hardware. Esta função retorna -1 se o dispositivo de áudio não puder ser aberto ou o fluxo de áudio não puder ser configurado.

Para abrir um dispositivo de áudio, você precisa criar o SDL_AudioSpec desejado. Então você precisa preencher essa estrutura com as especificações de áudio desejadas. desejado-

> freq : frequência sonora desejada em amostras por segundo. desejado-
> formato : formato de áudio desejado (consulte SDL_AudioSpec).
Canais desejados: Canais necessários (1 para mono, 2 para estéreo, 4 para som surround, 6 para som surround com centralização e LFE). desejado-
> amostras : tamanho do buffer de áudio desejado nas amostras. Esse número deve ter uma potência de dois e pode ser ajustado pelo driver de áudio para um valor mais adequado para o hardware. Os valores ideais variam de 512 a 8192, inclusive, dependendo da velocidade do aplicativo e do processador. Valores menores resultam em tempos de resposta mais rápidos, mas podem levar a um desempenho ruim se o aplicativo executar um processamento pesado e não conseguir preencher o buffer de áudio a tempo. A amostra estéreo consiste nos canais direito e esquerdo na ordem LR. Observe que o número de amostras está diretamente relacionado ao tempo usando a seguinte fórmula: ms = (amostras * 1000) / freq .
desejado-> retorno de chamada : deve ser definido para uma função que será chamada quando a unidade de áudio estiver pronta para receber dados adicionais. O ponteiro para o buffer de áudio e o comprimento em bytes do buffer de áudio são transmitidos. Essa função geralmente é executada em um thread separado e, portanto, é necessário proteger as estruturas de dados acessadas chamando SDL_LockAudio e SDL_UnlockAudio no código. O protótipo de retorno de chamada é um retorno de chamada nulo ( void * userdata , Uint8 * stream , int len ) . userdata - um ponteiro armazenado no campo userdata SDL_AudioSpec. correnteÉ um ponteiro para o buffer de áudio que você deseja preencher com informações e len é o tamanho do buffer de áudio em bytes.
required-> userdata : esse ponteiro é passado como o primeiro parâmetro para a função de retorno de chamada.

SDL_OpenAudio lê esses campos da estrutura SDL_AudioSpec desejada passada para a função e tenta encontrar a configuração de áudio que corresponde ao seu desejo. Como mencionado acima, se o parâmetro resultante for NULL, o SDL será convertido das configurações de som desejadas para as configurações do equipamento durante a reprodução.

Se NULL for retornado, o SDL_AudioSpec necessário é sua especificação de trabalho; caso contrário, o SDL_AudioSpec resultante se tornará uma especificação de trabalho e a especificação desejada poderá ser excluída. Os dados na especificação de trabalho são usados ​​ao criar o SDL_AudioCVT para converter os dados baixados no formato do equipamento.

SDL_OpenAudio calcula os campos de tamanho e silêncio para a especificação desejada e a resultante. O campo tamanho armazena o tamanho total do buffer de áudio em bytes, enquanto o silêncio armazena o valor usado para representar o silêncio no buffer de áudio

O dispositivo de som começa a tocar silêncio quando está aberto e deve ser ativado para reprodução chamando SDL_PauseAudio (0) quando você estiver pronto para chamar a função de retorno de chamada de áudio. Como o driver de áudio pode alterar o tamanho solicitado do buffer de áudio, você deve selecionar os buffers do mixer local após abrir o dispositivo de áudio.

void SDL_PauseAudio(int pause_on)

Esta função pausa e interrompe o processamento de retorno de chamada de áudio. Ele deve ser chamado com pause_on = 0 após abrir o dispositivo de áudio para iniciar a reprodução do som. Isso permite que você inicialize com segurança os dados da função de retorno de chamada após abrir a unidade de áudio. O silêncio será gravado no dispositivo de áudio durante uma pausa.

int SDL_PushEvent(SDL_Event *event)

Uma fila de eventos que é realmente usada como um canal de comunicação bidirecional. Não apenas os eventos podem ser lidos na fila, mas o usuário também pode colocar seus próprios eventos nela. Um evento é um ponteiro para a estrutura do evento que você deseja enfileirar. O evento é copiado para a fila e o chamador pode gerenciar a memória apontada após retornar o SDL_PushEvent. Essa função é orientada a threads e pode ser chamada com segurança de outros threads. Retorna 0 se for bem-sucedido ou -1 se o evento não puder ser despachado.

int SDL_WaitEvent(SDL_Event *event)

Aguarda indefinidamente o próximo evento disponível, retornando 0 se ocorrer um erro ao aguardar eventos, 1 caso contrário. Se o evento não for NULL, o próximo evento será removido da fila e armazenado nesta área.

void SDL_Quit()

Desativa todos os subsistemas SDL e libera os recursos alocados a ele. Isso sempre deve ser chamado antes de sair.

SDL_Surface *SDL_SetVideoMode(int width, int height, int bitsperpixel, Uint32 flags)

Configuração do modo de vídeo com a largura, altura e bits de pixel especificados. A partir do SDL 1.2.10, se a largura e a altura forem 0, ele usará a largura e a altura do modo de vídeo atual (ou modo de área de trabalho se o modo não estiver definido). Se bitsperpixel for 0, ele será tratado como os bits de exibição atuais por pixel. O parâmetro flags é o mesmo que o campo flags da estrutura SDL_Surface. Ou uma combinação dos seguintes valores:

SDL_SWSURFACE - crie uma superfície de vídeo na memória do sistema.
SDL_HWSURFACE - crie uma superfície de vídeo na memória de vídeo.
SDL_ASYNCBLIT - permite o uso de atualizações assíncronas na superfície da tela. Isso geralmente diminui o trabalho em computadores de processador único, mas pode aumentar a velocidade nos sistemas SMP.
SDL_ANYFORMAT - normalmente, se uma superfície de vídeo com os bits solicitados por pixel (bpp - de bits por pixel) não estiver disponível, o SDL emulará o vídeo com uma superfície sombreada. Passar SDL_ANYFORMAT evita isso e força o SDL a usar a superfície do vídeo, independentemente da profundidade em pixels.
SDL_HWPALETTE - fornece acesso exclusivo ao SDL para a paleta. Sem esse sinalizador, você nem sempre pode obter as cores solicitadas usando SDL_SetColors ou SDL_SetPalette.
SDL_DOUBLEBUF - habilita o buffer duplo de hardware; válido apenas com SDL_HWSURFACE. Uma chamada para SDL_Flip reverterá os buffers e atualizará a tela. Todo o desenho ocorrerá em uma superfície que não é exibida no momento. Se o buffer duplo não puder ser ativado, o SDL_Flip simplesmente executará o SDL_UpdateRect em tela cheia.
SDL_FULLSCREEN SDL - tente usar o modo de tela cheia. Se não for possível alterar a resolução do hardware (por qualquer motivo), a próxima resolução mais alta será usada e a janela de exibição será centralizada em um fundo preto.
SDL_OPENGL - crie um contexto de renderização OpenGL. Supõe-se que os atributos de vídeo do OpenGL com SDL_GL_SetAttribute estejam predefinidos.
SDL_OPENGLBLIT - crie um contexto de renderização OpenGL, conforme descrito acima, mas permita operações normais de blitting. Uma superfície da tela (2D) pode ter um canal alfa e SDL_UpdateRects deve ser usado para atualizar as alterações na superfície da tela. NOTA. Esta opção é salva apenas para compatibilidade e será removida em versões futuras. Não recomendado para uso em novo código.
SDL_RESIZABL - crie uma janela redimensionável. Quando o tamanho da janela é alterado pelo usuário, o evento SDL_VIDEORESIZE é gerado e SDL_SetVideoMode pode ser chamado novamente com um novo tamanho.
SDL_NOFRAME Se possível, SDL_NOFRAME força o SDL a criar uma janela sem um título ou emoldurado. Este sinalizador é definido automaticamente no modo de tela cheia.
Nota. Independentemente de quais sinalizadores SDL_SetVideoMode possam atender, eles são configurados no elemento flags da superfície retornada.
NOTA. O pixel de bit 24 usa uma representação compactada de 3 bytes por pixel. Para o modo mais comum de 4 bytes por pixel, use um pixel de 32 bits. Por incrível que pareça, 15 e 16 solicitarão um modo de 2 bytes por pixel, mas com diferentes formatos de pixel.
NOTA. Use SDL_SWSURFACE se você planeja executar manipulações de pixel separadas ou arrastar superfícies usando canais alfa e exigir uma alta taxa de quadros. Quando você usa superfícies de hardware (SDL_HWSURFACE), o SDL copia superfícies da memória de vídeo para a memória do sistema quando você as bloqueia e vice-versa quando as desbloqueia. Isso pode levar a uma diminuição significativa no desempenho. (Lembre-se de que você pode consultar uma superfície de hardware, mas ainda obter uma superfície de software. Muitas plataformas só podem fornecer uma superfície de hardware ao usar SDL_FULLSCREEN.) SDL_HWSURFACE é melhor usado quando as superfícies que você pisca também podem ser armazenadas na memória de vídeo.
NOTA. Se você deseja controlar a posição na tela ao criar a superfície da janela, pode fazer isso definindo as variáveis ​​de ambiente “SDL_VIDEO_CENTERED = center” ou “SDL_VIDEO_WINDOW_POS = x, y”. Você pode instalá-los via SDL_putenv.

Valor de retorno: Superfície do buffer do quadro ou NULL em caso de falha. A superfície retornada é liberada pelo SDL_Quit e não deve ser liberada pelo chamador.
NOTA. Essa regra inclui chamadas consecutivas para SDL_SetVideoMode (ou seja, redimensionamento) - a superfície existente será liberada automaticamente.






Apêndice 2. Estruturas de Dados



AVCodecContext

Todas as informações sobre o codec do fluxo, do AVStream-> codec. Alguns atributos importantes:

AVRational time_base : número de quadros por segundo
int sample_rate : amostras por segundo
int channel : número de canais

Veja a lista completa (muito impressionante) aqui ( arquivo da web, pois o link original já não existe ). Muitos parâmetros são usados ​​principalmente para codificação, não para decodificação.

AVFormatContext

Campos de dados:

const AVClass * av_class
AVInputFormat * iformat
AVOutputFormat * oformat
void * priv_data :
ByteIOContext pb : usado para manipulação de arquivos de baixo nível.
unsigned int nb_streams : número de threads no arquivo.
Fluxos AVStream * [MAX_STREAMS] : os dados de cada fluxo são armazenados aqui.
char nome do arquivo [1024]: mas e sem ele (no original - duh ).

Informações do arquivo:
int64_t timestamp :
char title [512]:
char autor [512]:
char copyright [512]:
char comment [512]:
char album [512]:
int ano :
int faixa :
char gênero [32]:

int ctx_flags :
Os valores possíveis são AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, AVFMT_RAWPICTURE, AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_GENERIC_INDEX
AVPacketList * packet_buffer : esse buffer é necessário apenas quando os pacotes já estão em buffer, mas não decodificados, por exemplo, para receber parâmetros de codec em fluxos MPEG.
int64_tstart_time : when decoding: a posição do primeiro quadro do componente, em frações de segundo, AV_TIME_BASE. NUNCA defina esse valor diretamente: é inferido a partir dos valores do AVStream.
int64_t duration: decoding : duração do fluxo, em frações de AV_TIME_BASE. NUNCA defina esse valor diretamente: é inferido a partir dos valores do AVStream.
int64_t file_size : tamanho total do arquivo, 0 se desconhecido.
int bit_rate : decoding: taxa de bits total do fluxo em bit / s, 0 se não estiver disponível. NUNCA defina-o diretamente se o tamanho do arquivo e a duração conhecida no ffmpeg puderem calculá-lo automaticamente.
AVStream * cur_st
const uint8_t * cur_ptr
int cur_len
AVPacket cur_pkt :
int64_t data_offset :
int index_built : deslocamento do primeiro pacote.
int mux_rate :
int packet_size :
int preload :
int max_delay :
int loop_output : número de loops de saída nos formatos suportados.
sinalizadores int :
int loop_input :
não assinado int probesize : decodificação: tamanho dos dados da amostra; não usado na codificação.
int max_analyze_duration : duração máxima em unidades de AV_TIME_BASE durante a qual os dados de entrada devem ser analisados ​​em av_find_stream_info ()
const uint8_t * key :
int keylen :

AVIOContext

Contexto de E / S para acessar recursos.

const AVClass * av_class : classe para configurações particulares.
não assinado char * buffer : início do buffer.
int buffer_size : tamanho máximo do buffer.
char não assinado * buf_ptr : posição atual no buffer.
char não assinado * buf_end : Os dados podem ser menores que buffer + buffer_size se a função de leitura retornar menos dados do que o solicitado, por exemplo.
void * opaco : ponteiro privado passado para leitura / gravação / pesquisa / ...
int (* read_packet) (void * opaco, uint8_t * buf, int buf_size) :
int (* write_packet) (void * opaco, uint8_t * buf, int buf_size ) :
int64_t (* busca) (nulo * opaco, int64_t deslocamento, int donde) :
int64_t pos : posição no arquivo do buffer atual.
int must_flush : true se a próxima pesquisa deve ser redefinida.
int eof_reached : true se o final do arquivo for atingido.
int write_flag : true se aberto para gravação.
int max_packet_size :
soma de verificação longa não
assinada : soma de verificação longa não assinada : soma de verificação_ptr :
longa não assinada (* update_checksum) (soma de verificação longa não assinada, const uint8_t * buf, tamanho int não assinado) :
erro int : contém o código de erro ou 0 se nenhum erro ocorreu.
int (* read_pause) (void * opaco, int pause): pausar ou retomar a reprodução de protocolos de streaming em rede, por exemplo.
int64_t (* read_seek) (void * opaco, int stream_index, int64_t timestamp, int flags) : procure o timestamp especificado no fluxo com o índice stream_index especificado.
int seekable : combinação dos sinalizadores AVIO_SEEKABLE_ ou 0 quando o fluxo não é pesquisável.
int64_t maxsize : tamanho máximo do arquivo usado para limitar a seleção. Este campo é interno ao libavformat e o acesso a ele a partir do exterior é proibido.
int direct : avio_read e avio_write devem ser executados diretamente sempre que possível, e não passar pelo buffer, e avio_seek sempre chamará diretamente a principal função de pesquisa.
int64_t bytes_read: byte read statistics Esse campo é interno ao libavformat e o acesso externo é negado.
int seek_count : estatísticas de pesquisa. Este campo é interno ao libavformat e o acesso a ele a partir do exterior é proibido.
int writeout_count : escreve estatísticas. Este campo é interno ao libavformat e o acesso a ele a partir do exterior é proibido.
int orig_buffer_size : o tamanho original do buffer usado internamente após verificar e fornecer um retorno para redefinir o tamanho do buffer. Este campo é interno ao libavformat e o acesso a ele a partir do exterior é proibido.

AVDictionary

Usado para passar parâmetros para ffmpeg.

int count :
AVDictionaryEntry * elems :

AVDictionaryEntry

Usado para armazenar entradas de dicionário no AVDictionary.

char * ket :
char * value :

AVFrame

Essa estrutura depende do tipo de codec e, portanto, é determinada dinamicamente. No entanto, existem propriedades e métodos comuns para essa estrutura:

uint8_t * data [4] :
int tamanho da linha [4] : passo da informação.
uint8_t * base [4] :
int key_frame :
int pict_type :
int64_t pts : esses não são os pts que você espera ao decodificar.
int coded_picture_number :
int display_picture_number :
int qualidade :
int idade :
int referência :
int8_t * qscale_table :
int qstride :
uint8_t * mbskip_table :
int16_t (* motion_val [2]) [2]:
uint32_t * mb_type :
uint8_t motion_subsample_log2 :
void * opaco : dados do usuário
erro uint64_t [4] :
int tipo :
int repeat_pict : instrui você a repetir a imagem um número especificado de vezes.
int qscale_type :
int interlaced_frame :
int top_field_first :
AVPanScan * pan_scan :
int palette_has_changed :
int buffer_hints :
short * dct_coeff :
int8_t * ref_index [2] :

AVPacket

A estrutura na qual os dados brutos do pacote são armazenados. Esses dados devem ser transferidos para avcodec_decode_audio2 ou avcodec_decode_video para receber um quadro.

int64_t pts : registro de data e hora da apresentação em unidades time_base.
int64_t dts : registro de data e hora de descompactação em unidades de time_base.
uint8_t * data : dados brutos.
int size : tamanho dos dados.
int stream_index : fluxo do qual AVPacket veio, com base na quantidade em AVFormatContext.
sinalizadores int : PKT_FLAG_KEY é definido se o pacote for um quadro-chave.
int duration : duração da apresentação em unidades de time_base (0 se não disponível)
void (* destruct) (struct AVPacket *) : função de liberação de recurso para este pacote (av_destruct_packet por padrão).
void * priv :
int64_t pos : posição de bytes no fluxo, -1 se desconhecido.

AVPacketList

Uma lista vinculada simples para pacotes.

AVPacket pkt :
AVPacketList * próximo :

AVPicture

Essa estrutura é exatamente a mesma que os dois primeiros elementos de dados do AVFrame, portanto é frequentemente descartada. Comumente usado em funções SWS.

uint8_t * data [4] :
int tamanho da linha [4] : o número de bytes na string.

AVRational

Estrutura simples para representar números racionais.

int num : numerador.
int den : denominador.

AVStream

A estrutura para o fluxo. É provável que você use essas informações no codec com mais frequência.

int index :
int id :
AVCodecContext * codec :
AVRational r_frame_rate :
void * priv_data :
codec_info_duration int64_t :
codec_info_nb_frames int :
pts AVFrac :
time_base AVRational :
pts_wrap_bits int :
int stream_copy :
uma enumeração AVDiscard o descarte : você pode escolher os pacotes que serão jogados fora porque eles não precisam na desmultiplexação.
qualidade do flutuador :
int64_t start_time :
int64_t duration:
Char idioma [4] :
need_parsing int : 1 -> precisa de análise completo, 2 -> apenas cabeçalhos de análise, sem mudança de embalagem
AVCodecParserContext * analisador :
int64_t cur_dts :
int last_IP_duration :
last_IP_pts int64_t :
AVIndexEntry * index_entries :
nb_index_entries int :
um int index_entries_allocated_size não assinado :
int64_t nb_frames : número de quadros neste fluxo (se conhecido) ou 0
int64_t pts_buffer [MAX_REORDER_DELAY + 1] :

ByteIOContext

Uma estrutura que armazena informações de baixo nível sobre um arquivo de filme.

char não assinado * buffer :
int buffer_size :
char não assinado * buf_ptr :
char não assinado * buf_end :
void * opaco :
int (* read_packet) (void * opaco, uint8_t * buf, int buf_size) :
int (* write_packet) (void * opaque, uint8_t * buf, int buf_size) :
offset_t (* seek) (void * opaco, offset_t offset, int whence) :
offset_t pos :
int must_flush :
int eof_reached :
int write_flag :
int is_streamed :
int max_packet_size :
soma de verificação longa não
assinada : char não assinado * checksum_ptr :
longa não assinada (* update_checksum) (soma de verificação longa não assinada:
const uint8_t * buf, tamanho int não assinado)
:
int error : contém o código de erro ou 0 se nenhum erro ocorreu.

SDL_AudioSpec

Usado para descrever o formato de alguns dados de áudio.

freq : frequência do som em amostras por segundo.
formato : formato de dados de áudio.
canais : número de canais: 1 - mono, 2 - estéreo, 4 surround, 6 surround com centralização e
silêncio LFE : valor de silêncio do buffer de som (calculado).
samples : tamanho do buffer de áudio em samples.
tamanho : o tamanho do buffer de áudio em bytes (calculado).
retorno de chamada (..) : função de retorno de chamada para preencher o buffer de áudio.
userdata : um ponteiro para os dados do usuário que são passados ​​para a função de retorno de chamada.

Os seguintes valores de formato são

válidos : AUDIO_U8 - ​​amostras não assinadas de 8 bits.
AUDIO_S8 - amostras de 8 bits assinadas.
AUDIO_U16 ou AUDIO_U16LSB - não suportado por todo o hardware (ordem de bytes baixos não assinados de 16 bits).
AUDIO_S16 ou AUDIO_S16LS - não suportado por todo o hardware (16 bits com ordem de bytes antigos)
AUDIO_U16MSB - não suportado por todo o hardware (big endian não assinado de 16 bits).
AUDIO_S16MS - não é suportado por todo o hardware (16 bits com alta ordem de bytes).
AUDIO_U16SYS: AUDIO_U16LSB ou AUDIO_U16MSB - dependendo do processador de hardware.
AUDIO_S16SYS: AUDIO_S16LSB ou AUDIO_S16MSB - dependendo do processador de hardware.

SDL_Event

A estrutura básica para eventos.

tipo : tipo de evento.
active : evento de ativação (consulte SDL_ActiveEvent).
key : evento do teclado (consulte SDL_KeyboardEvent).
motion : evento de movimento do mouse (consulte SDL_MouseMotionEvent).
botão : evento de clique do mouse (consulte SDL_MouseButtonEvent).
jaxis : evento de movimento do eixo do joystick (consulte SDL_JoyAxisEvent).
jball : evento de movimento do trackball do joystick (consulte SDL_JoyBallEvent).
jhat : evento de movimento do cabeçalho do joystick (consulte SDL_JoyHatEvent).
jbutton : evento de pressionar o botão do joystick (consulte SDL_JoyButtonEvent).
redimensionar: Evento de redimensionamento da janela do aplicativo (consulte SDL_ResizeEvent).
exposição : evento aberto da janela do aplicativo (consulte SDL_ExposeEvent).
quit : evento de solicitação de saída do aplicativo (consulte SDL_QuitEvent).
usuário : evento do usuário (consulte SDL_UserEvent).
syswm : evento indefinido do gerenciador de janelas (consulte SDL_SysWMEvent).

Aqui estão os tipos de eventos. Consulte a documentação SDL para mais informações :.

SDL_ACTIVEEVENT SDL_ActiveEvent
SDL_KEYDOWN / UP SDL_KeyboardEvent
SDL_MOUSEMOTION SDL_MouseMotionEvent
SDL_MOUSEBUTTONDOWN / UP SDL_MouseButtonEvent
SDL_JOYAXISMOTION SDL_JoyAxisEvent
SDL_JOYBALLMOTION SDL_JoyBallEvent
SDL_JoyHatEvent SDL_JOYHATMOTION
SDL_JOYBUTTONDOWN / UP SDL_JoyButtonEvent
SDL_VIDEORESIZE SDL_ResizeEvent
SDL_VIDEOEXPOSE SDL_ExposeEvent
SDL_Quit SDL_QuitEvent
SDL_USEREVENT SDL_UserEvent
SDL_SYSWMEVENT SDL_SysWMEvent

SDL_Overlay

Sobreposição de YUV.

formato : formato de sobreposição (veja abaixo).
w, h : largura / altura da sobreposição.
planos : o número de planos a serem sobrepostos. Geralmente 1 ou 3.
arremessos : uma matriz de recuos, um para cada plano. Recuo é o comprimento da sequência em bytes.
pixels : uma matriz de ponteiros de dados para cada plano. A sobreposição deve estar bloqueada antes de usar esses ponteiros.
hw_overlay : defina como 1 se a sobreposição for acelerada por hardware.

SDL_Rect

A área retangular.

Sint16 x, y : posição do canto superior esquerdo do retângulo.
Uint16 w, h : a largura e a altura do retângulo.

SDL_Rect define uma área retangular de pixels. É usado pelo SDL_BlitSurface para identificar áreas de blitting e alguns outros recursos de vídeo.

SDL_Surface

Estrutura gráfica do lado externo (superfície).

Sinalizadores Uint32 : Sinalizadores dos stotrons externos. Somente para leitura.
Formato SDL_PixelFormat * : somente leitura.
int w, h : largura e altura. Somente para leitura.
Passo Uint16 : passo. Somente para leitura.
void * pixels : um ponteiro para os dados de pixel reais. Apenas para gravação.
SDL_Rect clip_rect : a parte externa retangular do clipe. Somente para leitura.
int refcount : usado para alocar memória. Principalmente para leitura.
Essa estrutura também contém campos particulares não mostrados aqui.

SDL_Surface representa uma área da memória "gráfica" que pode ser desenhada. O quadro do buffer de vídeo é retornado como SDL_Surface usando SDL_SetVideoMode e SDL_GetVideoSurface. Os campos w e h são valores que representam a largura e a altura da superfície em pixels. O campo de pixel é um ponteiro para os dados de pixel reais. Nota: a superfície deve estar bloqueada (via SDL_LockSurface) antes de acessar este campo. O campo clip_rect é o retângulo recortado definido por SDL_SetClipRect.

O campo sinalizador suporta os seguintes valores OR:

SDL_SWSURFACE - o exterior é armazenado na memória do sistema.
SDL_HWSURFACE - O lado externo é armazenado na memória de vídeo.
SDL_ASYNCBLIT - A parte externa usa brilho assíncrono, se possível.
SDL_ANYFORMAT - Qualquer formato de pixel (superfície da tela) é permitido.
SDL_HWPALETTE - A superfície possui uma paleta exclusiva.
SDL_DOUBLEBUF - superfície com buffer duplo (superfície da tela).
SDL_FULLSCREEN - superfície de tela cheia (superfície de exibição).
SDL_OPENGL - a superfície possui um contexto OpenGL (superfície de exibição).
SDL_OPENGLBLIT - a superfície suporta blinting OpenGL (superfície da tela). NOTA. Esta opção é apenas para compatibilidade e não é recomendada para novo código.
SDL_RESIZABLE - é possível redimensionar uma superfície (superfície da tela).
SDL_HWACCEL - o blit de superfície usa aceleração de hardware.
SDL_SRCCOLORKEY - A superficialidade usa cores cegas.
SDL_RLEACCEL - a divisão de cores é acelerada usando o RLE.
SDL_SRCALPHA - Surface Blyth usa mistura alfa.
SDL_PREALLOC - A superfície usa memória pré-alocada.

SDL_Thread

Essa estrutura é independente do sistema e você provavelmente não precisa usá-la. Veja src / thread / sdl_thread_c.h no código fonte para mais informações.

SDL_cond

Essa estrutura é independente do sistema e você provavelmente não precisa usá-la. Consulte src / thread / <system> /SDL_syscond.c no código fonte para obter mais informações.

SDL_mutex

Essa estrutura é independente do sistema e você provavelmente não precisa usá-la. Veja src / thread / <system> /SDL_sysmutex.c no código fonte para obter mais informações.






Links


Um tutorial sobre FFmpeg e SDL ou Como gravar um player de vídeo em menos de 1000 linhas

FFmpeg , SDL FFmpeg HomePage SDL HomePage











Leia também no blog da empresa EDISON:


Manual do FFmpeg libav

All Articles