[Bagian 2/2] Panduan untuk FFmpeg dan SDL atau Cara menulis pemutar video dalam waktu kurang dari 1000 baris


Kami memposting sisa terjemahan ke dalam bahasa Rusia manual, yang agak ketinggalan jaman, tetapi tidak kehilangan relevansinya, karena tutorial ini membantu untuk menggali ke dalam "dapur" membuat aplikasi video menggunakan perpustakaan FFmpeg dan SDL.

Dan meskipun kami sudah mencoba, kesulitan penerjemahan tidak dapat dihindari dalam teks yang begitu banyak . Laporkan bug (lebih disukai dalam pesan pribadi) - bersama-sama kami akan melakukan yang lebih baik.

Daftar Isi


Perangkat Lunak EDISON - pengembangan 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;
}


Sekarang kita memiliki pemain yang kurang lebih layak di mana Anda bahkan dapat menonton film, sekarang mari kita memenuhi kebutuhan. Terakhir kali, kami sedikit menyentuh pada sinkronisasi, yaitu, sinkronisasi suara dengan video, dalam urutan itu, bukan sebaliknya. Kami akan membuat tata letak ini sama dengan video: buat jam video internal untuk melacak seberapa jauh aliran video dan menyinkronkan audio dengannya. Nanti kami akan menggeneralisasi lebih banyak lagi - kami menyinkronkan audio dan video dengan jam eksternal.

Implementasi Jam Video


Sekarang kami ingin membuat jam video yang mirip dengan jam audio yang kami miliki terakhir kali: nilai internal yang mengembalikan offset waktu saat ini dari video yang sedang diputar. Anda mungkin berpikir bahwa itu akan sesederhana memperbarui timer dengan PTS saat ini dari frame terakhir yang ditampilkan. Namun, jangan lupa bahwa waktu antara bingkai video mungkin terlalu lama jika kita turun ke tingkat milidetik. Oleh karena itu, solusinya adalah melacak nilai lain, waktu di mana kami menyetel jam video pada PTS dari frame terakhir. Dengan demikian, nilai jam video saat ini adalah PTS_of_last_frame + ( current_time - time_elapsed_since_PTS_value_was_set) Solusi ini sangat mirip dengan apa yang kami lakukan dengan get_audio_clock .

Jadi, dalam struktur full-blown kami, kami akan menempatkan video_current_pts ganda dan int64_t video_current_pts_time . Jam akan diperbarui dalam fungsi 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();

Jangan lupa untuk menginisialisasi di stream_component_open :

is->video_current_pts_time = av_gettime();

Dan sekarang yang kita butuhkan adalah beberapa cara untuk mendapatkan informasi:

double get_video_clock(VideoState *is) {
  double delta;

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

Mengabstraksi dari arloji


Tapi mengapa memaksakan diri untuk menggunakan jam video? Anda dapat melangkah lebih jauh dan mengubah kode sinkronisasi video kami sehingga audio dan video tidak mencoba menyinkronkan satu sama lain. Bayangkan betapa berantakannya jika kita mencoba melakukan ini dengan opsi baris perintah, seperti pada FFplay. Jadi, mari kita abaikan : kita akan membuat fungsi wrapper baru, get_master_clock , yang memeriksa variabel av_sync_type , dan kemudian memanggil get_audio_clock , get_video_clock, atau jam lain yang bisa menggunakannya. Kami bahkan dapat menggunakan jam komputer, yang kami sebut 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;
...
}

Sinkronisasi audio


Sekarang bagian tersulit: sinkronisasi audio dengan jam video. Strategi kami adalah mengukur di mana audio berada, membandingkannya dengan jam video, dan kemudian mencari tahu berapa banyak sampel yang perlu kami sesuaikan, yaitu, apakah kami perlu mempercepat dengan menjatuhkan sampel atau memperlambat dengan menambahkan?

Kami menjalankan fungsi syncize_audio setiap kali kami memproses setiap set sampel audio yang kami dapatkan untuk mengurangi atau meningkatkan set ini dengan benar. Namun, kami tidak ingin menyinkronkan sepanjang waktu, karena pemrosesan audio terjadi jauh lebih sering daripada pemrosesan paket video. Jadi, kita akan mengatur jumlah minimum panggilan berturut-turut ke fungsi syncize_audioyang dianggap tidak disinkronkan sebelum kita repot melakukan apa pun. Tentu saja, seperti yang terakhir kali, "tidak disinkronkan" berarti bahwa jam audio dan jam video berbeda dengan jumlah yang lebih besar daripada ambang sinkronisasi.

Jadi kita akan menggunakan koefisien fraksional, katakanlah, s , dan sekarang, katakanlah kita mendapat Nset sampel audio yang tidak sinkron. Jumlah sampel yang tidak kami sinkronkan juga dapat sangat bervariasi, jadi kami mengambil nilai rata-rata dari masing-masing sampel yang tidak disinkronkan. Misalnya, panggilan pertama dapat menunjukkan bahwa kita tidak disinkronkan untuk 40 ms, selanjutnya untuk 50 ms dan seterusnya. Tetapi kita tidak akan mengambil makna yang sederhana, karena nilai-nilai terbaru lebih penting daripada nilai-nilai yang datang sebelumnya. Jadi, kita akan menggunakan koefisien fraksional, katakanlah, c , dan rangkum perbedaannya sebagai berikut: diff_sum = new_diff + diff_sum * c . Ketika kami siap untuk menemukan perbedaan rata-rata, kami cukup menghitung avg_diff =diff_sum * (1 - c ).

Apa yang sedang terjadi di sini? Persamaannya terlihat seperti semacam sihir. Nah, ini pada dasarnya adalah rata-rata tertimbang menggunakan deret geometris sebagai bobot. Saya tidak tahu apakah ada nama untuk ini (saya bahkan memeriksa di Wikipedia!), Tapi untuk informasi lebih lanjut, ini penjelasannya (atau di sini: weightedmean.txt ).

Begini fungsi kami:

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

Jadi semuanya baik-baik saja dengan kami; kami tahu kira-kira seberapa banyak suara tidak konsisten dengan video atau dengan apa yang kami gunakan sebagai jam tangan. Jadi, sekarang mari kita hitung berapa banyak sampel yang perlu kita tambahkan atau buang dengan menempatkan kode ini di bagian โ€œMengurangi / memperluas kode 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;
  }

Ingat bahwa audio_length * ( sample_rate * # channel * 2) adalah jumlah sampel dalam audio_length detik audio. Oleh karena itu, jumlah sampel yang kita inginkan akan sama dengan jumlah sampel yang sudah kita miliki, plus atau minus jumlah sampel yang sesuai dengan jumlah waktu selama suara dimainkan. Kami juga akan menetapkan batas seberapa besar atau kecil koreksi kami, karena jika kami mengubah buffer terlalu banyak, itu akan terlalu mengganggu bagi pengguna.

Koreksi jumlah sampel


Sekarang kita perlu memperbaiki suaranya. Anda mungkin telah memperhatikan bahwa fungsi syncize_audio kami mengembalikan ukuran sampel, yang kemudian memberi tahu kami berapa banyak byte untuk dikirim ke aliran. Jadi kita hanya perlu menyesuaikan ukuran sampel dengan nilai yang diinginkan. Ini berfungsi untuk mengurangi ukuran sampel. Tetapi jika Anda perlu meningkatkannya, kami tidak bisa hanya menambah ukuran sampel, karena tidak ada lagi data di buffer! Karena itu, kita harus menambahkan sedikit. Tapi apa sebenarnya yang ditambahkan? Akan bodoh mencoba mengekstrapolasi audio, jadi mari kita gunakan audio yang sudah kita miliki, menambahkan nilai sampel terakhir ke 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;
}

Sekarang kita mengembalikan ukuran sampel, dan kita selesai dengan fungsi ini. Yang perlu kita lakukan sekarang adalah menggunakan ini:

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;

Yang kami lakukan hanyalah memasukkan panggilan syncize_audio . (Juga, pastikan untuk memeriksa kode sumber, di mana kami menginisialisasi variabel yang saya tidak repot untuk mendefinisikan.)

Dan yang terakhir, sebelum kita selesai: kita perlu menambahkan kondisi "jika" untuk memastikan bahwa kita tidak menyinkronkan video jika itu jam utama:

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

Dan itu berhasil! Pastikan Anda memeriksa file sumber untuk menginisialisasi variabel apa pun yang saya tidak perlu mendefinisikan atau menginisialisasi. Kemudian kompilasi:

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

dan penerbangan akan normal.

Dalam pelajaran terakhir kita akan mundur.






Pelajaran 7: Cari โ† โ‡‘ โ†’


Daftar lengkap 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;

}

Cari Perintah Pemrosesan


Sekarang kita akan menambahkan beberapa kemampuan pencarian di pemutar kami, karena itu benar-benar menjengkelkan ketika Anda tidak dapat memundurkan film. Selain itu, kita akan melihat betapa mudahnya menggunakan fungsi av_seek_frame .

Kita akan membuat panah pada keyboard "kiri" dan "kanan" gulirkan film ke depan dan ke belakang sedikit, dan panah "ke atas" dan "ke bawah" sudah lebih signifikan. "Sedikit" - itu akan menjadi 10 detik, dan "banyak" - semua 60. Oleh karena itu, kita perlu mengkonfigurasi loop utama kita sehingga itu mencegat peristiwa keystroke. Tetapi faktanya adalah ketika kita mendapatkan penekanan tombol, kita tidak dapat memanggil av_seek_frame secara langsung. Ini harus dilakukan di loop decoding utama kami, loop decode_thread. Karenanya, sebagai gantinya, kami akan menambahkan beberapa nilai ke struktur utama, yang akan berisi posisi baru untuk pencarian dan beberapa tanda pencarian:

  int             seek_req;
  int             seek_flags;
  int64_t         seek_pos;

Sekarang kita perlu mengkonfigurasi loop utama kita yang menangkap penekanan tombol:

  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;

Untuk menangkap penekanan tombol, pertama-tama kita melihat apakah peristiwa SDL_KEYDOWN terjadi . Kemudian kami memeriksa kunci mana yang diterima menggunakan event.key.keysym.sym . Segera setelah kami mengetahui ke arah mana kami mencari, kami menghitung waktu baru, menambahkan kenaikan nilai dari fungsi get_master_clock baru kami . Kemudian kita memanggil fungsi stream_seek untuk mengatur nilai seek_pos , dll. Konversikan waktu baru kami ke unit timestamp internal avcodec . Ingat bahwa cap waktu dalam aliran diukur dalam bingkai, bukan detik, menggunakan rumus berikut: detik = bingkai * time_base ( fps ).Secara default, avcodec diatur ke 1.000.000 frame per detik (sehingga posisi 2 detik akan memiliki cap waktu 2.000.000). Mengapa kita perlu mengonversi nilai ini - lihat nanti.

Inilah fungsi stream_seek kami . Perhatikan bahwa kami mengatur bendera jika kami kembali:

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

Sekarang mari kita beralih ke decode_thread kita , di mana kita benar-benar melakukan pencarian. Di file sumber Anda dapat melihat bahwa kami telah menandai area "pencarian sedang berlangsung". Baiklah, kita akan meletakkannya di sana sekarang.

Pencarian terpusat di sekitar fungsi av_seek_frame . Fungsi ini sebagai argumen konteks format, aliran, stempel waktu, dan set bendera. Fungsi akan mencari cap waktu yang Anda berikan. Unit cap waktu adalah time_base dari aliran yang Anda lewati ke fungsi. Namun, Anda tidak perlu meneruskannya ke aliran (ditunjukkan dengan meneruskan nilai -1). Jika Anda melakukan ini, time_base akan berada di unit waktu internal avcodecatau 1000000fps. Itu sebabnya kami mengalikan posisi kami dengan AV_TIME_BASE ketika kami menetapkan seek_pos .

Namun, kadang-kadang Anda dapat (jarang) mengalami masalah untuk beberapa file jika Anda melewatkan av_seek_frame - 1 untuk streaming, jadi kami akan memilih aliran pertama di file kami dan meneruskannya ke av_seek_frame . Jangan lupa bahwa kita harus mengubah skala cap waktu kita agar berada di "sistem koordinat" yang baru.

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 ) adalah fungsi yang mengatur timestamp dari satu basis ke basis lainnya. Pada dasarnya menghitung a * b / c , tetapi fungsi ini sangat berguna karena perhitungan ini kadang-kadang menyebabkan overflow. AV_TIME_BASE_Q adalah versi fraksional dari AV_TIME_BASE . Mereka sangat berbeda: AV_TIME_BASE * time_in_seconds = avcodec_timestamp dan AV_TIME_BASE_Q * avcodec_timestamp = time_in_seconds (tetapi perhatikan bahwa AV_TIME_BASE_Qsebenarnya adalah objek AVRational , jadi Anda perlu menggunakan fungsi q khusus dalam avcodec untuk memprosesnya).

Pembersih Buffer


Jadi, kami mengatur pencarian kami dengan benar, tetapi belum selesai. Ingat, apakah kita memiliki antrian yang dikonfigurasi untuk mengakumulasi paket? Sekarang kita berada di timestamp yang berbeda, kita perlu menghapus antrian ini, jika tidak pencarian di film tidak akan berfungsi! Selain itu, avcodec memiliki buffer internal sendiri, yang juga perlu dibilas untuk setiap aliran.

Untuk melakukan ini, Anda harus terlebih dahulu menulis fungsi yang menghapus antrian paket kami. Maka Anda perlu entah bagaimana menginstruksikan aliran audio dan video bahwa mereka membersihkan buffer internal avcodec . Kita dapat melakukan ini dengan menempatkan paket khusus dalam antrian setelah dibersihkan, dan ketika mereka (utas) menemukan paket khusus ini, mereka hanya akan menghapus buffer mereka.

Mari kita mulai dengan fungsi reset. Ini benar-benar sangat sederhana, jadi saya hanya akan menunjukkan kode:

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

Sekarang setelah antrian dihapus, tambahkan "paket pembersihan" kami. Tetapi pertama-tama, akan menyenangkan untuk mendefinisikan apa itu dan membuatnya:

AVPacket flush_pkt;

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

Sekarang masukkan paket ini ke dalam antrian:

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

(Cuplikan kode ini melanjutkan cuplikan kode di atas untuk decode_thread .) Kita juga perlu memodifikasi packet_queue_put sehingga kami tidak menduplikasi paket khusus untuk pembersihan:

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

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

Dan kemudian dalam aliran audio dan video kami menempatkan panggilan ini ke avcodec_flush_buffers tepat setelah package_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;
    }

Cuplikan kode di atas persis sama untuk streaming video, dengan penggantian "audio" dengan "video".

Ini dia! Kita berhasil! Kompilasi pemain Anda:

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

dan nikmati pemutar film Anda yang dibuat dalam kurang dari 1000 baris C!

Meskipun, tentu saja, ada banyak hal yang dapat ditambahkan atau ditingkatkan.






Kata penutup โ† โ‡‘ โ†’


Jadi, kami punya pemain yang bekerja, tapi tentu saja itu tidak sebagus yang seharusnya. Dimungkinkan untuk memodifikasi file dan menambahkan banyak hal berguna:

  • Mari kita hadapi itu, pemain ini payah. Versi ffplay.c yang menjadi basisnya benar-benar ketinggalan zaman, dan sebagai hasilnya, tutorial ini perlu direvisi secara menyeluruh. Jika Anda ingin beralih ke proyek yang lebih serius menggunakan pustaka FFmpeg, saya sangat menyarankan Anda memeriksa versi terbaru dari ffplay.c sebagai tugas berikutnya.
  • Penanganan kesalahan dalam kode kami sangat buruk dan dapat diimplementasikan jauh lebih baik.
  • , , , . , paused , , . , , . av_read_play. - , . , , . : , ffplay.c.
  • .
  • . , , , , VOB-.
  • . , .
  • . .
  • , , , , YUV, time_base.
  • .
  • --, ; ffplay.c .


Jika Anda ingin tahu lebih banyak tentang FFmpeg, maka di sini kami telah mempertimbangkan jauh dari segalanya. Langkah selanjutnya adalah mempelajari pengkodean multimedia. Yang terbaik adalah memulai dengan file output_example.c , yang akan Anda temukan di distribusi FFmpeg. Saya sudah bisa menulis buku teks lain tentang topik ini, tetapi tidak mungkin melampaui panduan ini.

UPD.Beberapa waktu yang lalu saya tidak memperbarui teks ini, tetapi sementara itu dunia tidak tinggal diam. Tutorial ini hanya membutuhkan pembaruan API sederhana; sangat sedikit yang berubah dalam hal konsep dasar. Sebagian besar pembaruan ini sebenarnya menyederhanakan kode. Namun, meskipun saya telah mempelajari semua kode dan memperbaruinya, FFplay masih lebih unggul dari pemain mainan ini. Dengan sepenuh hati, kami akui: dalam pelajaran ini kami menulis pemain film yang sangat buruk. Karena itu, jika hari ini (atau di masa depan) Anda ingin meningkatkan tutorial ini, saya sarankan Anda membiasakan diri dengan FFplay dan mencari tahu apa yang hilang. Saya percaya bahwa ini terutama menyangkut penggunaan peralatan video, tetapi, sangat mungkin, saya kehilangan beberapa hal yang jelas lainnya. Mungkin perbandingan dengan FFplay saat ini akan mengarah pada penulisan ulang radikal beberapa hal - saya belum menontonnya.

Tetapi saya sangat bangga bahwa selama bertahun-tahun pekerjaan saya telah banyak membantu, bahkan dengan mempertimbangkan fakta bahwa orang sering mencari kode di tempat lain. Saya sangat berterima kasih kepada Chelyaev , yang mengambil sendiri rutinitas untuk mengganti semua fungsi yang sudah ketinggalan zaman sejak saya menulis monograf ini 8 (!) Bertahun-tahun yang lalu.

Saya bersukacita dengan harapan bahwa pelajaran ini ternyata bermanfaat dan tidak membosankan. Jika ada saran, kesalahan, keluhan, terima kasih, dll. Mengenai panduan ini, silakan menulis kepada saya di dranger dog gmail dot com. Dan ya, tidak masuk akal untuk meminta saya untuk membantu proyek FFmpeg Anda. Terlalu banyak surat serupa .






Lampiran 1. Daftar fungsi โ† โ‡‘ โ†’


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

Buka nama file media, simpan konteks format di alamat yang ditentukan dalam ptr .

fmt : jika bukan NULL, maka atur format file.
buf_size : ukuran buffer (opsional).
Pilihan : AVDictionary diisi dengan parameter AVFormatContext dan demultiplexer.

void avformat_close_input(AVFormatContext **s)

Menutup file media. Namun, itu tidak menutup codec.

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

Membuat konteks I / O untuk menggunakan sumber daya yang ditentukan dalam url .

s : arahkan ke tempat AVIOContext akan dibuat . Dalam hal kegagalan, nilai yang ditentukan diatur ke NULL.
url : nama sumber daya yang akan diakses.
flags : mengontrol pembukaan sumber daya yang ditentukan dalam url .
int_cb : interupsi panggilan balik untuk penggunaan tingkat protokol.
Pilihan : kamus yang diisi dengan parameter protokol pribadi. Ketika fungsi kembali, parameter akan dihancurkan dan diganti dengan dict yang berisi opsi yang tidak ditemukan. Mungkin NULL.

int av_dup_packet(AVPacket *pkt)

Tentu saja, ini adalah retasan: jika paket ini belum dialokasikan, kami mempostingnya di sini. Mengembalikan 0 pada kesuksesan atau AVERROR_NOMEM pada kegagalan.

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

Fungsi ini mencari informasi aliran yang tidak jelas, seperti frame rate. Ini berguna untuk format file tanpa header seperti MPEG. Disarankan untuk menelepon setelah membuka file. Mengembalikan> = 0 jika berhasil, AVERROR_ * jika terjadi kesalahan.

AVFrame *avcodec_free_frame()

Nama lama untuk av_frame_free. Diubah dalam lavc 55.28.1.

void av_frame_free (AVFrame **frame)

Rilis bingkai dan objek apa pun yang dialokasikan secara dinamis di dalamnya, misalnya, extended_data.

void av_free(void *ptr)

Merilis memori yang dialokasikan menggunakan av_malloc () atau av_realloc (). Anda dapat memanggil fungsi ini dengan ptr == NULL. Disarankan agar Anda memanggil av_freep () sebagai gantinya.

void av_freep(void *ptr)

Bebaskan memori dan setel pointer ke NULL. Secara internal menggunakan av_free ().

void av_free_packet(AVPacket *pkt)

Bungkus metode penghancuran paket (pkt-> destruct).

int64_t av_gettime()

Dapatkan waktu saat ini dalam mikrodetik.

void av_init_packet(AVPacket *pkt)

Inisialisasi bidang paket opsional.

void *av_malloc(unsigned int size)

Ukuran byte alokasi memori dengan perataan cocok untuk semua akses memori (termasuk vektor, jika tersedia pada CPU). av_malloc (0) harus mengembalikan pointer bukan nol.

void *av_mallocz(unsigned int size)

Sama seperti av_malloc (), tetapi menginisialisasi memori ke nol.

double av_q2d(AVRational a)

Ganda AVRational.

int av_read_frame(AVFormatContext *s, AVPacket *pkt)

Mengembalikan bingkai aliran berikutnya. Informasi disimpan sebagai paket di pkt.

Paket yang dikembalikan valid hingga av_read_frame () berikutnya atau hingga av_close_input_file () dan harus dibebaskan menggunakan av_free_packet. Untuk paket video berisi persis satu frame. Untuk audio, ini berisi jumlah bingkai integer jika setiap frame memiliki ukuran tetap yang diketahui (misalnya, data PCM atau ADPCM). Jika frame audio berukuran variabel (misalnya, audio MPEG), maka ia mengandung satu frame.

pkt-> pts, pkt-> dts dan pkt-> durasi selalu diatur ke nilai yang benar dalam unit AVStream.timebase (dan diasumsikan bahwa format tidak dapat menyediakannya). pkt-> pts dapat berupa AV_NOPTS_VALUE jika format video memiliki bingkai B, jadi lebih baik mengandalkan pkt-> dts jika Anda tidak membongkar muatan.

Hasil yang dikembalikan: 0, jika semuanya baik-baik saja, <0, jika ada kesalahan atau akhir file.

void av_register_all();

Mendaftarkan semua codec di perpustakaan.

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

Mengembalikan sebuah * bq / cq .

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

Mencari keyframe di stempel waktu.

stream_index : jika stream_index adalah -1, aliran default dipilih dan stempel waktu secara otomatis dikonversi dari AV_TIME_BASE unit ke time_base khusus aliran.
timestamp : timestamp yang diukur dalam satuan AVStream.time_base atau, jika tidak ada aliran yang ditentukan, maka dalam satuan AV_TIME_BASE.
flags : atur parameter berkenaan dengan arah dan mode pencarian:
AVSEEK_FLAG_ANY: cari dalam bingkai apa saja, bukan hanya yang utama.
AVSEEK_FLAG_BACKWARD: cari di arah yang berlawanan.
AVSEEK_FLAG_BYTE: Cari berdasarkan posisi dalam byte.

AVFrame *avcodec_alloc_frame()

Nama lama untuk av_frame_alloc. Diubah dalam lavc 55.28.1.

AVFrame *av_frame_alloc()

Pilih AVFrame dan inisialisasi. Dapat dibebaskan menggunakan av_frame_free ().

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

Mendekode bingkai audio dari avpkt ke bingkai. Fungsi avcodec_decode_audio4 () menerjemahkan file audio dari AVPacket. Untuk decoding, codec audio digunakan, yang dikaitkan dengan avctx menggunakan avcodec_open2 (). Frame hasil dekode disimpan dalam AVFrame yang ditentukan. Jika bingkai telah dibongkar, itu akan mengatur got_frame_ptr ke 1.

Peringatan: buffer input, data avpkt->, harus FF_INPUT_BUFFER_PADDING_SIZE lebih besar dari byte pembacaan yang sebenarnya, karena beberapa pembaca bitstream yang dioptimalkan membaca 32 atau 64 bit sekaligus dan dapat membaca hingga tamat.

avctx : konteks codec.
bingkai : bingkai target.
got_frame_ptr : target int, yang akan ditetapkan jika frame dibongkar.
AVPKT: AVPacket yang berisi audio.

Hasil yang dikembalikan: jika kesalahan dikembalikan, nilai negatif dikembalikan, jika tidak, jumlah byte yang digunakan dari input AVPacket dikembalikan.

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

Mendekode bingkai video dari buf ke gambar. Fungsi avcodec_decode_video2 () mendekodekan bingkai video dari buffer input berukuran buf_size. Untuk decoding, codec video digunakan, yang dikaitkan dengan avctx menggunakan avcodec_open2 (). Bingkai yang didekodekan yang dihasilkan disimpan dalam gambar.

Peringatan: contoh pelurusan dan masalah buffer yang berlaku untuk avcodec_decode_audio4 juga berlaku untuk fungsi ini.

avctx : konteks codec.
gambar : AVFrame tempat video yang didekodekan akan disimpan.
frameFinished : nol jika tidak ada bingkai yang bisa dibuka, kalau tidak tidak sama dengan nol.
avpkt: input AVPacket yang berisi buffer input. Anda dapat membuat paket seperti itu menggunakan av_init_packet (), kemudian, setelah menentukan data dan ukurannya, beberapa decoder mungkin juga membutuhkan bidang lain, seperti flag & AV_PKT_FLAG_KEY. Semua decoder dirancang untuk menggunakan sesedikit mungkin bidang.

Hasil yang dikembalikan: Pada kesalahan, nilai negatif dikembalikan, jika tidak jumlah byte yang digunakan atau nol jika tidak ada frame yang dapat didekompresi.

int64_t av_frame_get_best_effort_timestamp (const AVFrame *frame)

Metode akses sederhana untuk mendapatkan best_effort_timestamp dari objek AVFrame.

AVCodec *avcodec_find_decoder(enum CodecID id)

Mencari decoder dengan CodecID. Mengembalikan NULL pada kesalahan. Itu harus dipanggil setelah mendapatkan AVCodecContext yang diperlukan dari aliran di AVFormatContext menggunakan codecCtx-> codec_id.

void avcodec_flush_buffers(AVCodecContetx *avctx)

Buffer flush. Dipanggil saat mencari atau beralih ke aliran lain.

AVCodecContext * avcodec_alloc_context3 (const AVCodec *codec)

Tetapkan AVCodecContext dan setel bidangnya ke nilai default.

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

Salin pengaturan AVCodecContext sumber ke target AVCodecContext. Konteks yang dihasilkan dari codec tujuan akan ditutup, mis. Anda harus memanggil avcodec_open2 () sebelum Anda menggunakan AVCodecContext ini untuk mendekode / menyandikan data video / audio.

dest : harus diinisialisasi dengan avcodec_alloc_context3 (NULL), jika tidak maka tidak akan diinisialisasi.

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

Menginisialisasi avctx untuk menggunakan codec yang ditentukan dalam codec . Harus digunakan setelah avcodec_find_decoder. Mengembalikan nol pada kesuksesan dan nilai negatif pada kesalahan.

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

Mengatur struktur yang ditunjukkan gambar, dengan buffer ptr , format pix_fmt, dan lebar dan tinggi yang ditentukan. Mengembalikan ukuran data gambar dalam byte.

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

Menghitung berapa byte yang diperlukan untuk gambar dengan lebar, tinggi, dan format gambar tertentu.

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

Mengembalikan SwsContext untuk digunakan di sws_scale.

srcW , srcH , srcFormat : lebar, tinggi dan format piksel yang diinginkan.
dstW , dstH , dstFormat : lebar, tinggi dan format piksel akhir.
flags : metode penskalaan untuk digunakan.
Opsi berikut tersedia: SWS_FAST_BILINEAR, SWS_BILINEAR, SWS_BICUBIC, SWS_X, SWS_POINT, SWS_AREA, SWS_BICUBLIN, SWS_GAUSS, SWS_SINC, SWS_LANCZOS, SWS_SPLINE.
Bendera lain termasuk bendera kemampuan CPU: SWS_CPU_CAPS_MMX, SWS_CPU_CAPS_MMX2, SWS_CPU_CAPS_3DNOW, SWS_CPU_CAPS_ALTIVEC.
Bendera lainnya termasuk (saat ini belum sepenuhnya diimplementasikan) SWS_FULL_CHR_H_INT, SWS_FULL_CHR_H_INP dan SWS_DIRECT_BGR.
Akhirnya, ada SWS_ACCURATE_RND dan mungkin yang paling berguna untuk pemula, SWS_PRINT_INFO.
Saya tidak tahu apa yang kebanyakan dari mereka lakukan. Mungkin menulis kepada saya?
srcFilter , dstFilter : SwsFilter untuk sumber dan tujuan. SwsFilter memungkinkan penyaringan warna / kecerahan. Nilai standarnya adalah NULL.
param : harus menjadi pointer ke buffer [2] int dengan koefisien. Tidak didokumentasikan. Tampaknya digunakan untuk sedikit mengubah algoritma penskalaan standar. Nilai standarnya adalah NULL. Hanya untuk para ahli!

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

Timbangan data dalam src sesuai dengan pengaturan kami di SwsContext kami * c .
srcStride dan dstStride adalah ukuran baris sumber dan tujuan.

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

Menambahkan fungsi panggilan balik yang berjalan setelah jumlah milidetik yang ditentukan. Fungsi panggilan balik melewati interval timer saat ini dan parameter yang disediakan pengguna dari panggilan SDL_AddTimer dan mengembalikan interval timer berikutnya. (Jika nilai balik dari callback cocok dengan nilai yang diteruskan, timer akan terus bekerja pada kecepatan yang sama.) Jika nilai balik dari callback adalah 0, timer dibatalkan.
Cara lain untuk membatalkan timer saat ini adalah memanggil SDL_RemoveTimer dengan pengidentifikasi timer (yang dikembalikan dari SDL_AddTimer).

Fungsi panggilan balik waktu dapat dijalankan pada utas berbeda dari program utama Anda, dan karenanya tidak boleh memanggil fungsi apa pun dari dirinya sendiri. Namun, Anda selalu dapat memanggil SDL_PushEvent.

Tingkat perincian dari pengatur waktu tergantung pada platform, tetapi Anda harus berharap bahwa itu setidaknya 10 ms, karena ini adalah nilai yang paling umum. Ini berarti bahwa jika Anda meminta timer 16 ms, panggilan balik akan dimulai setelah sekitar 20 ms pada sistem yang tidak dimuat. Jika Anda perlu mengatur bendera yang memberi sinyal pembaruan bingkai pada kecepatan 30 frame per detik (setiap 33 ms), Anda dapat mengatur timer selama 30 ms (lihat contoh di bawah). Jika Anda menggunakan fungsi ini, Anda harus meneruskan SDL_INIT_TIMER ke SDL_Init.

Mengembalikan nilai pengidentifikasi untuk penghitung waktu yang ditambahkan, atau NULL jika terjadi kesalahan.

Format untuk panggilan balik:
Uint32 callback ( Uint32, void * param)


int SDL_CondSignal(SDL_cond *cond)

Mulai ulang salah satu utas menunggu variabel kondisi kond . Mengembalikan 0 pada keberhasilan dan -1 pada kesalahan.

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

Buka kunci mutex yang disediakan dan tunggu utas lainnya untuk memanggil SDL_CondSignal atau SDL_CondBroadcast untuk variabel kondisi kond, kemudian mengunci kembali mutex. Mutex harus dikunci sebelum memasuki fungsi ini. Mengembalikan 0 ketika sinyal diterima, atau -1 pada kesalahan.

SDL_cond *SDL_CreateCond(void);

Membuat variabel kondisi.

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

SDL_CreateThread membuat utas eksekusi baru yang berbagi seluruh memori global induknya, penangan sinyal, deskriptor file, dll. Dan itu menjalankan fungsi fn , meneruskannya data void pointer. Utas berakhir ketika fn mengembalikan nilai.

void SDL_Delay (Uint32 );

Menunggu jumlah milidetik yang ditentukan. SDL_Delay akan menunggu setidaknya waktu yang ditentukan, tetapi mungkin lebih lama karena perencanaan OS.
Catatan: mengharapkan keterlambatan granularity minimal 10 ms. Beberapa platform memiliki ukuran yang lebih pendek, tetapi ini adalah opsi yang paling umum.

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

SDL_CreateYUVOverlay membuat overlay YUV dengan lebar, tinggi, dan format yang ditentukan (untuk daftar format yang tersedia, lihat struktur data SDL_Overlay) untuk tampilan yang disediakan. Mengembalikan SDL_Overlay.

tampilan sebenarnya adalah permukaan yang berasal dari SDL_SetVideoMode, jika tidak, fungsi ini akan berfungsi secara default.

Istilah "overlay" tidak benar, karena jika overlay tidak dibuat di perangkat keras, konten permukaan tampilan di bawah area tempat overlay ditampilkan akan ditimpa ketika overlay ditampilkan.

int SDL_LockYUVOverlay(SDL_Overlay *overlay)

SDL_LockYUVOverlay memblokir overlay untuk akses langsung ke data piksel. Mengembalikan 0 pada keberhasilan atau -1 pada kesalahan.

void SDL_UnlockYUVOverlay(SDL_Overlay *overlay)

Membuka kunci hamparan yang sebelumnya dikunci. Hamparan harus dibuka kuncinya sebelum dapat ditampilkan.

int SDL_DisplayYUVOverlay(SDL_Overlay *overlay, SDL_Rect *dstrect)

Letakkan overlay pada permukaan yang ditentukan saat dibuat. Struktur dstrect SDL_Rect menentukan posisi dan ukuran tujuan. Jika overlay lebih atau kurang, maka overlay akan diskalakan, ini dioptimalkan untuk penskalaan 2x. Mengembalikan 0 jika berhasil.

void SDL_FreeYUVOverlay(SDL_Overlay *overlay)

Merilis overlay yang dibuat oleh SDL_CreateYUVOverlay.

int SDL_Init(Uint32 flags);

Menginisialisasi SDL. Ini harus dipanggil sebelum semua fungsi SDL lainnya. Parameter flag menentukan bagian SDL mana yang akan diinisialisasi.

SDL_INIT_TIMER - menginisialisasi subsistem timer.
SDL_INIT_AUDIO - menginisialisasi subsistem audio.
SDL_INIT_VIDEO - menginisialisasi subsistem video.
SDL_INIT_CDROM - menginisialisasi subsistem CD-ROM.
SDL_INIT_JOYSTICK - menginisialisasi subsistem joystick.
SDL_INIT_EVERYTHING - Menginisialisasi semua yang di atas.
SDL_INIT_NOPARACHUTE - tidak mengizinkan SDL menangkap kesalahan fatal.
SDL_INIT_EVENTTHREAD - meluncurkan pengelola acara di utas terpisah.

Mengembalikan -1 pada kesalahan atau 0 pada keberhasilan. Anda bisa mendapatkan pesan kesalahan yang diperluas dengan memanggil SDL_GetError. Penyebab khas kesalahan adalah penggunaan tampilan tertentu tanpa dukungan yang sesuai untuk subsistem, misalnya, tidak adanya driver mouse saat menggunakan penyangga bingkai dengan perangkat. Dalam hal ini, Anda dapat mengkompilasi SDL tanpa mouse, atau mengatur variabel lingkungan "SDL_NOMOUSE = 1" sebelum memulai aplikasi.

SDL_mutex *SDL_CreateMutex(void);

Membuat mutex baru yang tidak dikunci.

int SDL_LockMutex(SDL_mutex *mutex)

SDL_LockMutex adalah alias untuk SDL_mutexP. Itu memblokir mutex yang sebelumnya dibuat menggunakan SDL_CreateMutex. Jika mutex sudah diblokir oleh utas lain, maka SDL_mutexP tidak mengembalikan nilai sampai utas diblokir olehnya membuka kunci itu (menggunakan SDL_mutexV). Ketika mutex dipanggil lagi, SDL_mutexV (alias SDL_UnlockMutex) harus disebut jumlah yang sama kali untuk mengembalikan mutex ke keadaan tidak terkunci. Mengembalikan 0 pada keberhasilan atau -1 pada kesalahan.

int SDL_UnlockMutex(SDL_Mutex *mutex)

Buka kunci Mutex.

int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)

Fungsi ini membuka unit audio dengan parameter yang diperlukan dan mengembalikan 0 jika berhasil, menempatkan parameter perangkat keras yang sebenarnya dalam struktur yang akhirnya ditunjukkan. Jika nilai NULL diterima, data audio yang diteruskan ke fungsi panggilan balik akan dijamin memiliki format yang diperlukan dan, jika perlu, akan secara otomatis dikonversi ke format audio perangkat keras. Fungsi ini mengembalikan -1 jika perangkat audio tidak dapat dibuka atau aliran audio tidak dapat dikonfigurasi.

Untuk membuka perangkat audio, Anda perlu membuat SDL_AudioSpec yang diinginkan. Maka Anda perlu mengisi struktur ini dengan spesifikasi audio yang diinginkan. diinginkan-

> freq : frekuensi suara yang diinginkan dalam sampel per detik.
format yang diinginkan- > : format audio yang diinginkan (lihat SDL_AudioSpec).
diinginkan-> saluran: Saluran yang diperlukan (1 untuk mono, 2 untuk stereo, 4 untuk suara surround, 6 untuk suara surround dengan centering dan LFE). diinginkan-
> sampel : ukuran buffer audio yang diinginkan dalam sampel. Angka ini harus dua kekuatan dan dapat disesuaikan oleh driver audio ke nilai yang lebih cocok untuk perangkat keras. Nilai optimal berkisar dari 512 hingga 8192 inklusif, tergantung pada aplikasi dan kecepatan prosesor. Nilai yang lebih kecil menghasilkan waktu respons yang lebih cepat, tetapi dapat menyebabkan kinerja yang buruk jika aplikasi melakukan pemrosesan berat dan tidak dapat mengisi buffer audio tepat waktu. Sampel stereo terdiri dari saluran kanan dan kiri dalam urutan LR. Harap perhatikan bahwa jumlah sampel berhubungan langsung dengan waktu menggunakan rumus berikut: ms = (sampel * 1000) / freq .
diinginkan-> panggilan balik : harus diatur ke fungsi yang akan dipanggil ketika unit audio siap menerima data tambahan. Pointer ke buffer audio dan panjang dalam byte buffer audio ditransmisikan. Fungsi ini biasanya dieksekusi di utas terpisah, dan oleh karena itu diperlukan untuk melindungi struktur data yang diaksesnya dengan memanggil SDL_LockAudio dan SDL_UnlockAudio dalam kode. Prototipe callback adalah void callback ( void * userdata , Uint8 * stream , int len ) . userdata - pointer yang disimpan di bidang data pengguna SDL_AudioSpec. aliranMerupakan pointer ke buffer audio yang ingin Anda isi dengan informasi, dan len adalah panjang buffer audio dalam byte.
diperlukan-> userdata : pointer ini dilewatkan sebagai parameter pertama ke fungsi panggilan balik.

SDL_OpenAudio membaca bidang ini dari struktur SDL_AudioSpec yang diinginkan diteruskan ke fungsi dan mencoba menemukan konfigurasi audio yang sesuai dengan keinginan Anda. Seperti disebutkan di atas, jika parameter yang dihasilkan NULL, maka SDL dikonversi dari pengaturan suara yang diinginkan ke pengaturan peralatan selama pemutaran.

Jika NULL dikembalikan, maka SDL_AudioSpec yang diperlukan adalah spesifikasi kerja Anda, jika tidak, SDL_AudioSpec yang dihasilkan menjadi spesifikasi yang berfungsi, dan spesifikasi yang diinginkan dapat dihapus. Data dalam spesifikasi kerja digunakan saat membangun SDL_AudioCVT untuk mengubah data yang diunduh menjadi format peralatan.

SDL_OpenAudio menghitung bidang ukuran dan keheningan untuk spesifikasi yang diinginkan dan yang dihasilkan. Bidang ukuran menyimpan ukuran total buffer audio dalam byte, sementara diam menyimpan nilai yang digunakan untuk merepresentasikan keheningan dalam buffer audio

Perangkat suara mulai diputar tanpa suara saat terbuka dan harus dinyalakan untuk diputar dengan memanggil SDL_PauseAudio (0) saat Anda siap memanggil fungsi panggilan balik audio. Karena driver audio dapat mengubah ukuran buffer audio yang diminta, Anda harus memilih buffer mixer lokal setelah membuka perangkat audio.

void SDL_PauseAudio(int pause_on)

Fungsi ini menjeda dan menghentikan pemrosesan panggilan balik audio. Itu harus dipanggil dengan pause_on = 0 setelah membuka perangkat audio untuk mulai memutar suara. Ini memungkinkan Anda untuk menginisialisasi data dengan aman untuk fungsi panggilan balik setelah membuka unit audio. Diam akan direkam pada perangkat audio selama jeda.

int SDL_PushEvent(SDL_Event *event)

Antrian acara yang sebenarnya digunakan sebagai saluran komunikasi dua arah. Tidak hanya peristiwa yang dapat dibaca dari antrian, tetapi pengguna juga dapat memasukkan acara mereka sendiri di dalamnya. Suatu acara adalah penunjuk ke struktur acara yang ingin Anda antri. Acara disalin ke antrian, dan pemanggil dapat mengatur memori yang ditunjukkan setelah mengembalikan SDL_PushEvent. Fungsi ini berorientasi pada ulir dan dapat dipanggil dengan aman dari utas lainnya. Mengembalikan 0 jika berhasil, atau -1 jika acara tidak dapat dikirim.

int SDL_WaitEvent(SDL_Event *event)

Menunggu tanpa batas waktu untuk acara yang tersedia berikutnya, mengembalikan 0 jika terjadi kesalahan saat menunggu acara, 1 sebaliknya. Jika acara bukan NULL, acara berikutnya dihapus dari antrian dan disimpan di area ini.

void SDL_Quit()

Menonaktifkan semua subsistem SDL dan membebaskan sumber daya yang dialokasikan untuknya. Ini harus selalu dipanggil sebelum keluar.

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

Pengaturan mode video dengan lebar, tinggi, dan bit piksel yang ditentukan. Mulai dari SDL 1.2.10, jika lebar dan tinggi adalah 0, ia akan menggunakan lebar dan tinggi dari mode video saat ini (atau mode desktop jika mode tidak diatur). Jika bitsperpixel adalah 0, itu diperlakukan sebagai tampilan bit saat ini per pixel. Parameter flags sama dengan field flags dari struktur SDL_Surface. Atau kombinasi dari nilai-nilai berikut:

SDL_SWSURFACE - buat permukaan video dalam memori sistem.
SDL_HWSURFACE - buat permukaan video dalam memori video.
SDL_ASYNCBLIT - memungkinkan penggunaan pembaruan asinkron ke permukaan tampilan. Ini biasanya memperlambat pekerjaan pada komputer prosesor tunggal, tetapi dapat meningkatkan kecepatan dalam sistem SMP.
SDL_ANYFORMAT - biasanya, jika permukaan video dengan bit yang diminta per pixel (bpp - dari bits-per-pixel) tidak tersedia, SDL akan meniru video dengan permukaan yang diarsir. Melewati SDL_ANYFORMAT mencegah hal ini dan memaksa SDL untuk menggunakan permukaan video, terlepas dari kedalamannya dalam piksel.
SDL_HWPALETTE - Menyediakan akses eksklusif SDL ke palet. Tanpa tanda ini, Anda tidak bisa selalu mendapatkan warna yang Anda minta menggunakan SDL_SetColors atau SDL_SetPalette.
SDL_DOUBLEBUF - mengaktifkan buffering perangkat keras ganda; hanya valid dengan SDL_HWSURFACE. Panggilan ke SDL_Flip akan membalikkan buffer dan menyegarkan layar. Semua gambar akan berlangsung di permukaan yang saat ini tidak ditampilkan. Jika buffering ganda tidak dapat diaktifkan, maka SDL_Flip hanya akan menjalankan layar penuh SDL_UpdateRect.
SDL_FULLSCREEN SDL - coba gunakan mode layar penuh. Jika mengubah resolusi perangkat keras tidak dimungkinkan (untuk alasan apa pun), resolusi lebih tinggi berikutnya akan digunakan, dan jendela tampilan akan dipusatkan pada latar belakang hitam.
SDL_OPENGL - buat konteks render OpenGL. Diasumsikan bahwa atribut video OpenGL dengan SDL_GL_SetAttribute sudah ditentukan sebelumnya.
SDL_OPENGLBLIT - buat konteks rendering OpenGL, seperti yang dijelaskan di atas, tetapi izinkan operasi blitting normal. Permukaan layar (2D) dapat memiliki saluran alfa, dan SDL_UpdateRects harus digunakan untuk memperbarui perubahan permukaan layar. CATATAN. Opsi ini disimpan hanya untuk kompatibilitas dan akan dihapus di versi mendatang. Tidak direkomendasikan untuk digunakan dalam kode baru.
SDL_RESIZABL -membuat jendela resizable. Ketika ukuran jendela diubah oleh pengguna, acara SDL_VIDEORESIZE dihasilkan, dan SDL_SetVideoMode dapat dipanggil lagi dengan ukuran baru.
SDL_NOFRAME Jika memungkinkan, SDL_NOFRAME memaksa SDL untuk membuat jendela tanpa judul atau bingkai. Bendera ini secara otomatis diatur dalam mode layar penuh.
Catatan. Tidak peduli flag SDL_SetVideoMode mana yang dapat dipenuhi, flag tersebut diatur dalam elemen flag dari permukaan yang dikembalikan.
CATATAN. Bit pixel 24 menggunakan representasi 3 byte per piksel yang dikemas. Untuk mode 4 byte per pixel yang lebih umum, gunakan 32 bit pixel. Anehnya, baik 15 dan 16 akan meminta 2 byte per mode pixel, tetapi dengan format pixel yang berbeda.
CATATAN. Gunakan SDL_SWSURFACE jika Anda berencana untuk melakukan manipulasi piksel terpisah atau menyeret permukaan menggunakan saluran alfa dan memerlukan laju bingkai tinggi. Saat Anda menggunakan permukaan perangkat keras (SDL_HWSURFACE), SDL menyalin permukaan dari memori video ke memori sistem saat Anda mengunci dan kembali ketika Anda membuka kunci. Ini dapat menyebabkan penurunan kinerja yang signifikan. (Ingatlah bahwa Anda dapat meminta permukaan perangkat keras tetapi masih mendapatkan permukaan perangkat lunak. Banyak platform hanya dapat menyediakan permukaan perangkat keras saat menggunakan SDL_FULLSCREEN.) SDL_HWSURFACE paling baik digunakan saat permukaan yang Anda berkedip-kedipkan juga dapat disimpan dalam memori video.
CATATAN. Jika Anda ingin mengontrol posisi di layar saat membuat permukaan jendela, Anda dapat melakukan ini dengan mengatur variabel lingkungan โ€œSDL_VIDEO_CENTERED = centerโ€ atau โ€œSDL_VIDEO_WINDOW_POS = x, yโ€. Anda dapat menginstalnya melalui SDL_putenv.

Nilai kembali: Permukaan penyangga bingkai atau NULL jika terjadi kegagalan. Permukaan yang dikembalikan dibebaskan oleh SDL_Quit dan tidak boleh dibebaskan oleh pemanggil.
CATATAN. Aturan ini mencakup panggilan berurutan ke SDL_SetVideoMode (mis., Mengubah ukuran) - permukaan yang ada akan dibebaskan secara otomatis.






Lampiran 2. Struktur Data โ† โ‡‘ โ†’



AVCodecContext

Semua informasi tentang codec dari aliran, dari AVStream-> codec. Beberapa atribut penting:

AVRational time_base : jumlah frame per detik
int sample_rate : sampel per saluran
int kedua : jumlah saluran Lihat daftar lengkap (sangat mengesankan) di sini ( arsip web, karena tautan asli sudah tidak ada ). Banyak parameter yang digunakan terutama untuk encoding, bukan untuk decoding.



AVFormatContext

Bidang data:

const AVClass * av_class
AVInputFormat * iformat
AVOutputFormat * oformat
void * priv_data :
ByteIOContext pb : digunakan untuk manipulasi file tingkat rendah.
unsigned int nb_streams : jumlah utas dalam file.
AVStream * stream [MAX_STREAMS] : data untuk setiap stream disimpan di sini.
nama file char [1024]: tapi bagaimana tanpa itu (dalam aslinya - duh ).

Informasi File: cap waktu
int64_t : judul char [512]:

penulis char [512]: hak cipta
char [512]: komentar char [512]: album char [512]: int tahun : jalur int : genre char [32]: int ctx_flags : Nilai yang mungkin adalah AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, AVFMT_SHOW_IDS, AVFMT_RAWPICTURE, AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_GENERIC_INDEX AVPacketList * packet_buffer : Buffer ini diperlukan hanya ketika paket sudah buffer tetapi tidak diterjemahkan, misalnya, untuk menerima parameter codec di stream mpeg. int64_t









start_time : ketika decoding: posisi frame pertama komponen, dalam sepersekian detik, AV_TIME_BASE. JANGAN PERNAH mengatur nilai ini secara langsung: ini disimpulkan dari nilai-nilai AVStream.
durasi int64_t: decoding : durasi aliran, dalam fraksi AV_TIME_BASE. JANGAN PERNAH mengatur nilai ini secara langsung: ini disimpulkan dari nilai-nilai AVStream.
int64_t file_size : ukuran file total, 0 jika tidak diketahui.
int bit_rate : decoding: total bitrate dari stream dalam bit / s, 0 jika tidak tersedia. JANGAN PERNAH mengaturnya secara langsung jika file_size dan durasi yang dikenal dalam ffmpeg dapat menghitungnya secara otomatis.
AVStream * cur_st
const uint8_t * cur_ptr
int cur_len
AVPacket cur_pkt :
int64_t data_offset :
int index_built : offset dari paket pertama.
int mux_rate :
int packet_size :
int preload :
int max_delay :
int loop_output : jumlah loop output dalam format yang didukung.
flag int :
int loop_input :
unsigned int probesize : decoding: ukuran data sampel; tidak digunakan dalam pengkodean.
int max_analyze_duration : durasi maksimum dalam satuan AV_TIME_BASE di mana data input harus dianalisis dalam av_find_stream_info ()
const uint8_t * kunci :
int keylen :

AVIOContext

Konteks I / O untuk mengakses sumber daya.

const AVClass * av_class : kelas untuk pengaturan pribadi.
unsigned char * buffer : mulai dari buffer.
int buffer_size : ukuran buffer maksimum.
unsigned char * buf_ptr : posisi saat ini di buffer.
unsigned char * buf_end : Data mungkin lebih kecil dari buffer + buffer_size jika fungsi baca mengembalikan lebih sedikit data daripada yang diminta, misalnya.
batal * buram : penunjuk pribadi diteruskan untuk membaca / menulis / mencari / ...
int (* read_packet) (batal * buram, uint8_t * buf, int buf_size) :
int (* write_packet) (batal * opaque, uint8_t * buf, buf_size int ) :
int64_t (* seek) (void * opaque, int64_t offset, int
whence ) : int64_t pos : posisi dalam file buffer saat ini.
int must_flush : true jika pencarian berikutnya harus di-reset.
int eof_reached : true jika akhir file tercapai.
int write_flag : true jika terbuka untuk menulis.
int max_packet_size :
unsigned long checksum :
unsigned char * checksum_ptr :
unsigned long (* update_checksum) (unsigned long checksum, const uint8_t * buf, unsigned int size) :
kesalahan int : berisi kode kesalahan atau 0 jika tidak ada kesalahan terjadi.
int (* read_pause) (void * opaque, int jeda): jeda atau lanjutkan pemutaran untuk protokol streaming jaringan, misalnya.
int64_t (* read_seek) (batal * buram, int stream_index, int64_t timestamp, int flags) : cari stempel waktu yang ditentukan dalam aliran dengan indeks stream_index yang ditentukan.
int seekable : kombinasi dari AVIO_SEEKABLE_ atau 0 flags saat stream tidak dapat dicari.
int64_t maxsize : ukuran file maksimum yang digunakan untuk membatasi pilihan. Bidang ini internal untuk libavformat, dan akses ke sana dari luar dilarang.
int direct : avio_read dan avio_write harus dieksekusi secara langsung kapan saja memungkinkan, dan tidak melewati buffer, dan avio_seek akan selalu secara langsung memanggil fungsi pencarian utama.
int64_t bytes_read: byte read statistics Bidang ini bersifat internal untuk libavformat dan akses eksternal ditolak.
int seek_count : statistik pencarian. Bidang ini internal untuk libavformat, dan akses ke sana dari luar dilarang.
int writeout_count : tulis statistik. Bidang ini internal untuk libavformat, dan akses ke sana dari luar dilarang.
int orig_buffer_size : Ukuran buffer asli digunakan secara internal setelah memeriksa dan memberikan pengembalian untuk mengatur ulang ukuran buffer. Bidang ini internal untuk libavformat, dan akses ke sana dari luar dilarang.

AVDictionary

Digunakan untuk meneruskan parameter ke ffmpeg.

int count :
AVDictionaryEntry * elems :

AVDictionaryEntry

Digunakan untuk menyimpan entri kamus di AVDictionary.

nilai char * ket :
char * :

AVFrame

Struktur ini tergantung pada jenis codec dan karenanya ditentukan secara dinamis. Namun, ada properti dan metode umum untuk struktur ini:

uint8_t * data [4] :
int linesize [4] : langkah informasi.
uint8_t * base [4] :
int key_frame :
int pict_type :
int64_t pts : ini bukan pts yang Anda harapkan saat decoding.
int coded_picture_number :
int display_picture_number :
int quality :
int age :
referensi int :
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 * opaque : data pengguna
kesalahan uint64_t [4] :
tipe
int : int repeat_pict : menginstruksikan Anda untuk mengulangi gambar beberapa kali.
int qscale_type :
int interlaced_frame :
int top_field_first :
AVPanScan * pan_scan :
int palette_has_changed :
int buffer_hints :
pendek * dct_coeff :
int8_t * ref_index [2] :

AVPacket

Struktur tempat data paket mentah disimpan. Data ini harus ditransfer ke avcodec_decode_audio2 atau avcodec_decode_video untuk menerima bingkai.

int64_t poin : cap waktu presentasi dalam satuan time_base.
int64_t dts : cap waktu dekompresi dalam satuan time_base.
data uint8_t * : data mentah.
ukuran int : ukuran data.
int stream_index : aliran dari mana AVPacket berasal, berdasarkan kuantitas dalam AVFormatContext.
int flags : PKT_FLAG_KEY disetel jika paket adalah bingkai kunci.
durasi int : durasi presentasi dalam satuan time_base (0 jika tidak tersedia)
void (* destruct) (struct AVPacket *) : fungsi rilis sumber daya untuk paket ini (av_destruct_packet secara default).
void * priv :
int64_t pos : posisi byte dalam aliran, -1 jika tidak diketahui.

AVPacketList

Daftar tertaut sederhana untuk paket.

Pkt AVPacket :
AVPacketList * selanjutnya :

AVPicture

Struktur ini persis sama dengan dua elemen data AVFrame pertama, sehingga sering dibuang. Biasa digunakan dalam fungsi SWS.

uint8_t * data [4] :
int linesize [4] : jumlah byte dalam string.

AVRational

Struktur sederhana untuk mewakili bilangan rasional.

int num : pembilang.
int den : penyebut.

AVStream

Struktur untuk aliran. Anda paling sering menggunakan informasi ini dalam codec.

int index :
int id :
AVCodecContext * codec :
AVRational r_frame_rate :
void * priv_data :
codec_info_duration int64_t :
int codec_info_nb_frames :
Poin AVFrac :
time_base AVRational :
pts_wrap_bits int :
int stream_copy :
enum AVDiscard membuang yang : Anda dapat memilih paket yang akan dibuang karena mereka tidak perlu dalam demultiplexing.
kualitas float :
int64_t start_time :
durasi int64_t:
Char bahasa [4] :
int need_parsing : 1 -> perlu parsing penuh, 2 -> header parse saja, tanpa mengemas
AVCodecParserContext * parser :
int64_t cur_dts :
int last_IP_duration :
last_IP_pts int64_t :
AVIndexEntry * index_entries :
nb_index_entries int :
int index_entries_allocated_size unsigned :
int64_t nb_frames : jumlah bingkai dalam aliran ini (jika diketahui) atau 0
int64_t pts_buffer [MAX_REORDER_DELAY + 1] :

ByteIOContext

Struktur yang menyimpan informasi tingkat rendah tentang file film.

unsigned char * buffer :
int buffer_size :
unsigned char * buf_ptr :
unsigned char * buf_end :
void * opaque :
int (* read_packet) (batal * opaque, uint8_t * buf, int buf_size) :
int (* write_packet) (void * opaque, uint8_t * buf, int buf_size) :
offset_t (* seek) (void * opaque, offset_t offset, int
whence ) : offset_t pos :
int must_flush :
int eof_reached :
int write_flag :
int is_streamed :
int max_packet_size :
unsigned long checksum :
unsigned char * checksum_ptr :
unsigned long (* update_checksum) (unsigned long checksum:
const uint8_t * buf, ukuran int unsigned)
:
int error : berisi kode kesalahan atau 0 jika tidak ada kesalahan terjadi.

SDL_AudioSpec

Digunakan untuk menggambarkan format beberapa data audio.

freq : frekuensi suara dalam sampel per detik.
format : format data audio.
saluran : jumlah saluran: 1 - mono, 2 - stereo, 4 surround, 6 surround dengan centering dan LFE
silence : nilai diam buffer suara (dihitung).
sampel : ukuran buffer audio dalam sampel.
size : Ukuran buffer audio dalam byte (dihitung).
callback (..) : fungsi callback untuk mengisi buffer audio.
userdata : pointer ke data pengguna yang diteruskan ke fungsi panggilan balik.

Nilai format berikut ini

valid : AUDIO_U8 - โ€‹โ€‹sampel 8-bit yang tidak ditandatangani.
AUDIO_S8 - menandatangani sampel 8-bit.
AUDIO_U16 atau AUDIO_U16LSB - tidak didukung oleh semua perangkat keras (urutan byte rendah 16-bit yang tidak ditandatangani).
AUDIO_S16 atau AUDIO_S16LS - tidak didukung oleh semua perangkat keras (16-bit dengan urutan byte lama)
AUDIO_U16MSB - tidak didukung oleh semua perangkat keras (big-endian 16-bit yang tidak ditandatangani).
AUDIO_S16MS - tidak didukung oleh semua perangkat keras (16-bit dengan urutan byte tinggi).
AUDIO_U16SYS: AUDIO_U16LSB atau AUDIO_U16MSB - tergantung pada prosesor perangkat keras.
AUDIO_S16SYS: AUDIO_S16LSB atau AUDIO_S16MSB - tergantung pada prosesor perangkat keras.

SDL_Event

Struktur dasar untuk acara.

type : tipe acara.
aktif : acara aktivasi (lihat SDL_ActiveEvent).
kunci : acara keyboard (lihat SDL_KeyboardEvent).
motion : acara pergerakan mouse (lihat SDL_MouseMotionEvent).
tombol : acara klik mouse (lihat SDL_MouseButtonEvent).
jaxis : peristiwa pergerakan sumbu joystick (lihat SDL_JoyAxisEvent).
jball : acara pergerakan trackball joystick (lihat SDL_JoyBallEvent).
jhat : peristiwa pergerakan tajuk joystick (lihat SDL_JoyHatEvent).
jbutton : acara menekan tombol joystick (lihat SDL_JoyButtonEvent).
mengubah ukuran: Acara mengubah ukuran jendela aplikasi (lihat SDL_ResizeEvent).
expose : acara buka jendela aplikasi (lihat SDL_ExposeEvent).
berhenti : acara permintaan keluar aplikasi (lihat SDL_QuitEvent).
pengguna : acara pengguna (lihat SDL_UserEvent).
syswm : acara window manager yang tidak ditentukan (lihat SDL_SysWMEvent).

Berikut adalah jenis-jenis acara. Lihat dokumentasi SDL untuk informasi lebih lanjut :.

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_VIDEOEXPOSE
SDV_Lakukan SDL_Quit
SDL_Quit SDL_KAMI
TIDAK MENDAPAT
UANGKAT KAMU TIDAK PERNAH KELUAR

SDL_Overlay

Hamparan YUV.

format : format overlay (lihat di bawah).
w, h : Lebar / tinggi hamparan.
planes : jumlah rencana untuk overlay. Biasanya 1 atau 3.
pitches : array indentasi, satu untuk setiap rencana. Lekukan adalah panjang string dalam byte.
piksel : array pointer data untuk setiap paket. Hamparan harus dikunci sebelum menggunakan petunjuk ini.
hw_overlay : Setel ke 1 jika overlay dipercepat perangkat keras.

SDL_Rect

Area persegi panjang.

Sint16 x, y : posisi sudut kiri atas dari persegi panjang.
Uint16 w, h : lebar dan tinggi persegi panjang.

SDL_Rect mendefinisikan area piksel persegi panjang. Ini digunakan oleh SDL_BlitSurface untuk mengidentifikasi area blitting dan beberapa fitur video lainnya.

SDL_Surface

Struktur grafis dari sisi luar (permukaan).

Bendera Uint32 : Bendera stotron eksternal. Hanya untuk membaca.
Format SDL_PixelFormat * : hanya-baca.
int w, h : lebar dan tinggi. Hanya untuk membaca.
Nada Uint16 : langkah. Hanya untuk membaca.
void * piksel : penunjuk ke data piksel aktual. Hanya untuk merekam.
SDL_Rect clip_rect : Persegi panjang di luar klip. Hanya untuk membaca.
int refcount : digunakan untuk mengalokasikan memori. Sebagian besar untuk membaca.
Struktur ini juga mengandung bidang pribadi yang tidak ditampilkan di sini.

SDL_Surface mewakili area memori "grafik" yang dapat ditarik. Frame buffer video dikembalikan sebagai SDL_Surface menggunakan SDL_SetVideoMode dan SDL_GetVideoSurface. Bidang w dan h adalah nilai yang mewakili lebar dan tinggi permukaan dalam piksel. Bidang piksel adalah penunjuk ke data piksel aktual. Catatan: permukaan harus dikunci (melalui SDL_LockSurface) sebelum mengakses bidang ini. Bidang clip_rect adalah persegi panjang terpotong yang ditetapkan oleh SDL_SetClipRect.

Bidang bendera mendukung nilai ATAU berikut:

SDL_SWSURFACE - bagian luar disimpan dalam memori sistem.
SDL_HWSURFACE - Sisi eksternal disimpan dalam memori video.
SDL_ASYNCBLIT - Bagian luar menggunakan silau asinkron, jika memungkinkan.
SDL_ANYFORMAT - Setiap format piksel (permukaan tampilan) diizinkan.
SDL_HWPALETTE - Permukaan memiliki palet eksklusif.
SDL_DOUBLEBUF - permukaan buffered ganda (permukaan tampilan).
SDL_FULLSCREEN - permukaan layar penuh (permukaan tampilan).
SDL_OPENGL - permukaan memiliki konteks OpenGL (permukaan tampilan).
SDL_OPENGLBLIT - permukaan mendukung penyempurnaan OpenGL (permukaan tampilan). CATATAN. Opsi ini hanya untuk kompatibilitas dan tidak disarankan untuk kode baru.
SDL_RESIZABLE - mengubah ukuran dimungkinkan untuk suatu permukaan (permukaan tampilan).
SDL_HWACCEL - blit permukaan menggunakan akselerasi perangkat keras.
SDL_SRCCOLORKEY - Superficiality menggunakan blitting warna.
SDL_RLEACCEL - blitting warna dipercepat menggunakan RLE.
SDL_SRCALPHA - Surface Blyth menggunakan alpha blending.
SDL_PREALLOC - Permukaan menggunakan memori yang dialokasikan sebelumnya.

SDL_Thread

Struktur ini independen dari sistem, dan Anda mungkin tidak perlu menggunakannya. Lihat src / utas / sdl_thread_c.h dalam kode sumber untuk informasi lebih lanjut.

SDL_cond

Struktur ini independen dari sistem, dan Anda mungkin tidak perlu menggunakannya. Lihat src / utas / <sistem> /SDL_syscond.c dalam kode sumber untuk informasi lebih lanjut.

SDL_mutex

Struktur ini independen dari sistem, dan Anda mungkin tidak perlu menggunakannya. Lihat src / utas / <sistem> /SDL_sysmutex.c dalam kode sumber untuk informasi lebih lanjut.






Tautan โ† โ‡‘


Tutorial FFmpeg dan SDL atau Cara Menulis Video Player dalam Kurang dari 1000 Baris

FFmpeg , SDL FFmpeg HomePage SDL HomePage











Baca juga di blog perusahaan EDISON:


Manual libav FFmpeg

All Articles