[Teil 2/2] Anleitung zu FFmpeg und SDL oder Schreiben eines Videoplayers in weniger als 1000 Zeilen


Wir veröffentlichen den Rest der Übersetzung des Handbuchs ins Russische, das etwas veraltet ist, aber nicht an Relevanz verloren hat, da dieses Tutorial hilft, die „Küche“ beim Erstellen von Videoanwendungen mit den Bibliotheken FFmpeg und SDL zu verstehen.

Und obwohl wir es versucht haben, sind Schwierigkeiten bei der Übersetzung in einem so umfangreichen Text unvermeidlich . Fehler melden (vorzugsweise in privaten Nachrichten) - gemeinsam werden wir es besser machen.

Inhaltsverzeichnis


EDISON Software - Webentwicklung
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;
}


Jetzt, da wir einen mehr oder weniger anständigen Player haben, auf dem Sie sogar einen Film sehen können, kommen wir jetzt über die Runden. Beim letzten Mal haben wir die Synchronisation leicht angesprochen, nämlich die Synchronisation von Ton und Video in dieser Reihenfolge, nicht umgekehrt. Wir werden das gleiche Layout wie bei Video erstellen: Erstellen Sie eine interne Videotakt, um zu verfolgen, wie weit der Videostream entfernt ist, und synchronisieren Sie Audio damit. Später werden wir noch mehr verallgemeinern - wir synchronisieren Audio und Video mit einer externen Uhr.

Implementierung der Videowache


Jetzt möchten wir eine Videouhr erstellen, die der Audiouhr ähnelt, die wir zuletzt hatten: einen internen Wert, der den aktuellen Zeitversatz des aktuell wiedergegebenen Videos zurückgibt. Sie könnten denken, dass es so einfach ist, den Timer mit dem aktuellen PTS des zuletzt angezeigten Frames zu aktualisieren . Vergessen Sie jedoch nicht, dass die Zeit zwischen Videobildern zu lang sein kann, wenn wir auf das Millisekundenniveau fallen. Daher besteht die Lösung darin, einen anderen Wert zu verfolgen, den Zeitpunkt, zu dem wir die Videotakt auf dem PTS des letzten Frames einstellen . Somit ist der aktuelle Wert der Videotakt PTS_of_last_frame + ( current_time - time_elapsed_since_PTS_value_was_set) Diese Lösung ist sehr ähnlich zu dem, was wir mit get_audio_clock gemacht haben .

In unserer vollständigen Struktur werden wir also double video_current_pts und int64_t video_current_pts_time einfügen . Die Uhr wird in der Funktion video_refresh_timer aktualisiert :

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

Vergessen Sie nicht, es in stream_component_open zu initialisieren :

is->video_current_pts_time = av_gettime();

Und jetzt brauchen wir nur noch einen Weg, um die Informationen zu erhalten:

double get_video_clock(VideoState *is) {
  double delta;

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

Abstrahieren von der Uhr


Aber warum zwingen Sie sich, eine Videouhr zu benutzen? Sie können noch weiter gehen und unseren Videosynchronisationscode ändern, damit Audio und Video nicht versuchen, sich gegenseitig zu synchronisieren. Stellen Sie sich vor, was für ein Durcheinander es sein wird, wenn wir versuchen, dies mit einer Befehlszeilenoption wie in FFplay zu tun. Lassen Sie uns also abstrahieren: Wir erstellen eine neue Wrapper-Funktion, get_master_clock , die die Variable av_sync_type überprüft und dann get_audio_clock , get_video_clock oder eine andere Uhr aufruft, die sie verwenden könnte. Wir können sogar eine Computeruhr verwenden, die wir get_external_clock nennen :

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

Audio-Synchronisierung


Jetzt der schwierigste Teil: Audio mit der Videotakt synchronisieren. Unsere Strategie besteht darin, zu messen, wo sich das Audio befindet, es mit der Videotakt zu vergleichen und dann herauszufinden, wie viele Samples wir anpassen müssen, dh müssen wir beschleunigen, indem wir die Samples ablegen oder durch Hinzufügen verlangsamen?

Wir führen die Funktion synchronize_audio jedes Mal aus, wenn wir jeden Satz von Audio-Samples verarbeiten, den wir erhalten, um diesen Satz richtig zu reduzieren oder zu erhöhen. Wir möchten jedoch nicht immer synchronisieren, da die Audioverarbeitung viel häufiger erfolgt als die Verarbeitung von Videopaketen. Wir werden also die Mindestanzahl aufeinanderfolgender Aufrufe der Funktion synchronize_audio festlegenDiese werden als nicht synchronisiert betrachtet, bevor wir uns die Mühe machen, etwas zu tun. Natürlich bedeutet "Nicht-Synchronisation" wie beim letzten Mal, dass sich die Audio- und die Video-Uhr um einen Betrag unterscheiden, der größer als der Synchronisationsschwellenwert ist.

Also werden wir einen Bruchkoeffizienten verwenden, sagen wir s , und jetzt nehmen wir an, wir haben N.Sätze von Audio-Samples, die nicht synchron waren. Die Anzahl der Samples, die wir nicht synchronisieren, kann ebenfalls stark variieren. Daher nehmen wir den Durchschnittswert dafür, wie viel jedes von ihnen nicht synchronisiert ist. Zum Beispiel könnte der erste Aufruf zeigen, dass wir 40 ms lang nicht synchronisiert sind, der nächste 50 ms lang und so weiter. Aber wir werden keinen einfachen Mittelwert nehmen, weil die neuesten Werte wichtiger sind als die, die vor ihnen liegen. Wir werden also einen Bruchkoeffizienten verwenden , z. B. c , und die Unterschiede wie folgt zusammenfassen: diff_sum = new_diff + diff_sum * c . Wenn wir bereit sind, die durchschnittliche Differenz zu ermitteln, berechnen wir einfach avg_diff =diff_sum * (1 - c ).

Was zur Hölle geht hier vor? Die Gleichung sieht aus wie eine Art Magie. Nun, dies ist im Grunde ein gewichteter Durchschnitt, der eine geometrische Reihe als Gewichte verwendet. Ich weiß nicht, ob es einen Namen dafür gibt (ich habe sogar auf Wikipedia nachgesehen!), Aber für weitere Informationen hier eine Erklärung (oder hier: weightedmean.txt ).

So sieht unsere Funktion aus:

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

Also ist alles in Ordnung mit uns; Wir wissen ungefähr, inwieweit der Ton nicht mit dem Video oder dem, was wir als Uhr verwenden, übereinstimmt. Berechnen wir nun, wie viele Beispiele wir hinzufügen oder verwerfen müssen, indem wir diesen Code in den Abschnitt „Reduzieren / Erweitern des Puffercodes“ einfügen:

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

Denken Sie daran, dass audio_length * ( sample_rate * # von Kanal * 2) die Anzahl der Samples in audio_length Sekunden von Audio ist. Daher entspricht die Anzahl der gewünschten Samples der Anzahl der bereits vorhandenen Samples plus oder minus der Anzahl der Samples, die der Zeitspanne entsprechen, in der der Sound abgespielt wurde. Wir werden auch ein Limit festlegen, wie groß oder klein unsere Korrektur sein kann, denn wenn wir unseren Puffer zu stark ändern, wird dies für den Benutzer zu ärgerlich.

Korrektur der Anzahl der Proben


Jetzt müssen wir den Sound reparieren. Möglicherweise haben Sie bemerkt, dass unsere Funktion synchronize_audio eine Stichprobengröße zurückgibt, die uns dann mitteilt, wie viele Bytes an den Stream gesendet werden sollen. Wir müssen also nur die Stichprobengröße auf den gewünschten Wert einstellen. Dies reduziert die Stichprobengröße. Wenn Sie es jedoch erhöhen müssen, können wir nicht nur die Stichprobengröße erhöhen, da sich keine Daten mehr im Puffer befinden! Deshalb müssen wir ein wenig hinzufügen. Aber was genau hinzufügen? Es wäre dumm zu versuchen, das Audio zu extrapolieren. Verwenden wir also einfach das bereits vorhandene Audio und addieren den Wert des letzten Samples zum Puffer.

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

Jetzt geben wir die Stichprobengröße zurück und sind mit dieser Funktion fertig. Jetzt müssen wir nur noch Folgendes verwenden:

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;

Wir haben lediglich einen Aufruf von synchronize_audio eingefügt . (Überprüfen Sie auch unbedingt den Quellcode, in dem wir die Variablen initialisieren, die ich nicht definiert habe.)

Und die letzte, bevor wir fertig sind: Wir müssen die "if" -Bedingung hinzufügen, um sicherzustellen, dass wir das Video nicht synchronisieren, wenn es die Hauptuhr ist:

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

Und es funktioniert! Stellen Sie sicher, dass Sie die Quelldatei überprüfen, um alle Variablen zu initialisieren, die ich nicht definiert oder initialisiert habe. Dann kompilieren Sie:

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

und der Flug wird normal sein.

In der letzten Lektion werden wir zurückspulen.






Lektion 7: Suche


Vollständige Auflistung 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;

}

Suchbefehlsverarbeitung


Jetzt werden wir einige Suchfunktionen in unseren Player aufnehmen, da es sehr ärgerlich ist, wenn Sie den Film nicht zurückspulen können. Außerdem werden wir sehen, wie einfach es ist, die Funktion av_seek_frame zu verwenden .

Wir werden die Pfeile auf der Tastatur "links" und "rechts" machen, um den Film ein wenig vorwärts und rückwärts zu scrollen, und die Pfeile "hoch" und "runter" sind bereits bedeutender. "Ein wenig" - es sind 10 Sekunden und "viel" - alle 60. Daher müssen wir unsere Hauptschleife so konfigurieren, dass sie Tastenanschlagereignisse abfängt. Tatsache ist jedoch, dass wir av_seek_frame nicht direkt aufrufen können, wenn wir einen Tastendruck erhalten . Dies muss in unserer Hauptdecodierungsschleife decode_thread erfolgen. Daher fügen wir stattdessen der Hauptstruktur einige Werte hinzu, die eine neue Position für die Suche und einige Suchflags enthalten:

  int             seek_req;
  int             seek_flags;
  int64_t         seek_pos;

Jetzt müssen wir unsere Hauptschleife konfigurieren, die Tastenanschläge abfängt:

  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;

Um einen Tastendruck abzufangen, prüfen wir zunächst, ob das Ereignis SDL_KEYDOWN aufgetreten ist . Dann prüfen wir mit event.key.keysym.sym , welcher Schlüssel empfangen wird . Sobald wir herausfinden, in welche Richtung wir schauen, berechnen wir eine neue Zeit und fügen dem Wert unserer neuen Funktion get_master_clock ein Inkrement hinzu . Dann rufen wir die Funktion stream_seek auf, um die Werte für seek_pos usw. festzulegen . Konvertieren Sie unsere neue Zeit in avcodec interne Zeitstempeleinheiten . Denken Sie daran, dass Zeitstempel in Streams nach der folgenden Formel in Frames und nicht in Sekunden gemessen werden: Sekunden = Frames * time_base ( fps ).Standardmäßig ist avcodec auf 1.000.000 Bilder pro Sekunde eingestellt (eine Position von 2 Sekunden hat also einen Zeitstempel von 2.000.000). Warum müssen wir diesen Wert konvertieren - siehe später.

Hier ist unsere stream_seek Funktion . Beachten Sie, dass wir das Flag setzen, wenn wir zurückgehen:

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

Fahren wir nun mit unserem decode_thread fort , in dem wir tatsächlich eine Suche durchführen. In den Quelldateien sehen Sie, dass wir den Bereich "Suche läuft" markiert haben. Nun, wir werden es gleich dort platzieren.

Die Suche konzentriert sich auf die Funktion av_seek_frame . Diese Funktion verwendet als Argument den Formatkontext, den Stream, den Zeitstempel und den Flag-Satz. Die Funktion sucht nach dem Zeitstempel, den Sie ihm geben. Die Einheit des Zeitstempels ist die Zeitbasis des Streams, den Sie an die Funktion übergeben. Sie müssen es jedoch nicht an den Stream übergeben (angezeigt durch Übergabe des Werts -1). Wenn Sie dies tun, befindet sich time_base in der internen Zeiteinheit von avcodecoder 1000000fps. Deshalb haben wir unsere Position mit AV_TIME_BASE multipliziert, als wir seek_pos gesetzt haben .

Manchmal kann es jedoch (selten) zu Problemen bei einigen Dateien kommen, wenn Sie av_seek_frame - 1 für den Stream übergeben. Daher wählen wir den ersten Stream in unserer Datei aus und übergeben ihn an av_seek_frame . Vergessen Sie nicht, dass wir den Maßstab unseres Zeitstempels ändern müssen, um im neuen „Koordinatensystem“ zu sein.

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 ) ist eine Funktion, die den Zeitstempel von einer Basis zur anderen skaliert. Grundsätzlich wird a * b / c berechnet , aber diese Funktion ist praktisch, da diese Berechnung manchmal zu einem Überlauf führt. AV_TIME_BASE_Q ist eine Teilversion von AV_TIME_BASE . Sie sind völlig unterschiedlich: AV_TIME_BASE * time_in_seconds = avcodec_timestamp und AV_TIME_BASE_Q * avcodec_timestamp = time_in_seconds (beachten Sie jedoch, dass AV_TIME_BASE_Qist eigentlich ein AVRational- Objekt , daher müssen Sie spezielle q- Funktionen in avcodec verwenden , um es zu verarbeiten.

Pufferreinigung


Wir haben unsere Suche also korrekt eingerichtet, sind aber noch nicht fertig. Denken Sie daran, haben wir eine Warteschlange konfiguriert, um Pakete zu akkumulieren? Jetzt, da wir uns in einem anderen Zeitstempel befinden, müssen wir diese Warteschlange löschen, sonst funktioniert die Suche im Film nicht! Darüber hinaus verfügt avcodec über eigene interne Puffer, die für jeden Stream ebenfalls geleert werden müssen.

Dazu müssen Sie zuerst eine Funktion schreiben, die unsere Paketwarteschlange löscht. Dann müssen Sie den Audio- und Videostream irgendwie anweisen, dass die internen Avcodec- Puffer gelöscht wurden . Wir können dies tun, indem wir ein spezielles Paket in die Warteschlange stellen, nachdem es bereinigt wurde. Wenn sie (Threads) dieses spezielle Paket entdecken, löschen sie einfach ihre Puffer.

Beginnen wir mit der Reset-Funktion. Es ist wirklich ganz einfach, also zeige ich Ihnen nur den Code:

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

Nachdem die Warteschlange gelöscht wurde, fügen Sie unser "Reinigungspaket" hinzu. Aber zuerst wäre es schön zu definieren, was es ist und es zu erstellen:

AVPacket flush_pkt;

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

Stellen Sie nun dieses Paket in die Warteschlange:

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

(Dieses Code-Snippet setzt das obige Code-Snippet für decode_thread fort .) Wir müssen auch packet_queue_put ändern, damit wir kein spezielles Paket für die Reinigung duplizieren:

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

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

Und dann in den Audio- und Video - Stream setzen wir diesen Aufruf in avcodec_flush_buffers direkt nach packet_queue_get :

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

Das obige Code-Snippet ist für den Videostream genau das gleiche, wobei "Audio" durch "Video" ersetzt wird.

Das ist es! Wir haben es geschafft! Stellen Sie Ihren Player zusammen:

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

und genießen Sie Ihren Movie Player, der in weniger als 1000 Zeilen C erstellt wurde!

Obwohl es natürlich viele Dinge gibt, die hinzugefügt oder verbessert werden können.






Nachwort


Wir haben also einen funktionierenden Spieler, aber natürlich ist es nicht so gut wie es sein könnte. Es wäre möglich, die Datei zu ändern und viele nützliche Dinge hinzuzufügen:

  • Seien wir ehrlich, dieser Spieler ist scheiße. Die Version von ffplay.c, auf der es basiert, ist vollständig veraltet. Daher muss dieses Tutorial gründlich überarbeitet werden. Wenn Sie mit den FFmpeg-Bibliotheken zu ernsthafteren Projekten übergehen möchten, empfehle ich dringend, als nächste Aufgabe die neueste Version von ffplay.c zu überprüfen.
  • Die Fehlerbehandlung in unserem Code ist schrecklich und kann viel besser implementiert werden.
  • , , , . , paused , , . , , . av_read_play. - , . , , . : , ffplay.c.
  • .
  • . , , , , VOB-.
  • . , .
  • . .
  • , , , , YUV, time_base.
  • .
  • --, ; ffplay.c .


Wenn Sie mehr über FFmpeg erfahren möchten, dann haben wir hier weit von allem nachgedacht. Der nächste Schritt ist das Studium der Multimedia-Codierung. Beginnen Sie am besten mit der Datei output_example.c , die Sie in der FFmpeg-Distribution finden. Ich könnte bereits ein weiteres Lehrbuch zu diesem Thema schreiben, aber es ist unwahrscheinlich, dass es diesen Leitfaden übertrifft.

UPD.Vor langer Zeit habe ich diesen Text nicht aktualisiert, aber mittlerweile steht die Welt nicht still. Dieses Tutorial erfordert nur einfache API-Updates. An den Grundkonzepten hat sich sehr wenig geändert. Die meisten dieser Updates haben den Code tatsächlich vereinfacht. Obwohl ich den gesamten Code durchgesehen und aktualisiert habe, ist FFplay diesem Spielzeugspieler immer noch überlegen. Hand aufs Herz, wir geben zu: In diesen Lektionen haben wir einen ziemlich miesen Filmspieler geschrieben. Wenn Sie dieses Tutorial heute (oder in Zukunft) verbessern möchten, empfehle ich Ihnen, sich mit FFplay vertraut zu machen und herauszufinden, was fehlt. Ich glaube, dass dies hauptsächlich die Verwendung von Videogeräten betrifft, aber möglicherweise fehlen mir einige andere offensichtliche Dinge. Vielleicht würde ein Vergleich mit dem aktuellen FFplay zu einer radikalen Umschreibung einiger Dinge führen - ich habe es noch nicht gesehen.

Aber ich bin sehr stolz darauf, dass meine Arbeit im Laufe der Jahre viel geholfen hat, auch wenn man berücksichtigt, dass die Leute oft anderswo nach Code gesucht haben. Ich bin Chelyaev sehr dankbar, der es sich zur Aufgabe gemacht hat , alle Funktionen zu ersetzen , die seit dem Schreiben dieser Monographie vor 8 (!) Jahren veraltet sind.

Ich freue mich in der Hoffnung, dass sich diese Lektionen als nützlich und nicht langweilig erwiesen haben. Wenn es Vorschläge, Fehler, Beschwerden, Dankeschöns usw. zu diesem Handbuch gibt, schreiben Sie mir bitte an dranger dog gmail dot com. Und ja, es macht keinen Sinn, mich zu bitten, bei Ihrem FFmpeg-Projekt zu helfen. Es gibt zu viele ähnliche Buchstaben .






Anhang 1. Liste der Funktionen


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

Öffnet den Namen der Mediendatei und speichert den Formatkontext unter der in ptr angegebenen Adresse .

fmt : Wenn nicht NULL, wird das Dateiformat festgelegt.
buf_size : Puffergröße (optional).
Optionen : AVDictionary wird mit den Parametern von AVFormatContext und Demultiplexer gefüllt .

void avformat_close_input(AVFormatContext **s)

Schließt die Mediendatei. Die Codecs werden jedoch nicht geschlossen.

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

Erstellt einen E / A-Kontext für die Verwendung der in der URL angegebenen Ressource .

s : Zeiger auf die Stelle, an der der AVIOContext erstellt wird . Im Fehlerfall wird der angegebene Wert auf NULL gesetzt.
url : Name der Ressource, auf die zugegriffen werden soll.
Flags : Steuern Sie das Öffnen der in der URL angegebenen Ressource .
int_cb : Interrupt-Rückruf zur Verwendung auf Protokollebene.
Optionen : Ein Wörterbuch mit privaten Protokollparametern. Wenn die Funktion zurückkehrt, wird der Parameter zerstört und durch ein Diktat ersetzt, das Optionen enthält, die nicht gefunden wurden. Kann NULL sein.

int av_dup_packet(AVPacket *pkt)

Dies ist natürlich ein Hack: Wenn dieses Paket nicht zugewiesen wurde, veröffentlichen wir es hier. Gibt bei Erfolg 0 oder bei Fehler AVERROR_NOMEM zurück.

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

Diese Funktion sucht nach nicht offensichtlichen Stream-Informationen, wie z. B. der Bildrate. Dies ist nützlich für Headerless-Dateiformate wie MPEG. Es wird empfohlen, nach dem Öffnen der Datei aufzurufen. Gibt bei Erfolg> = 0 zurück, im Fehlerfall AVERROR_ *.

AVFrame *avcodec_free_frame()

Alter Name für av_frame_free. Geändert in lavc 55.28.1.

void av_frame_free (AVFrame **frame)

Gibt einen Frame und alle dynamisch zugewiesenen Objekte frei, z. B. Extended_Data.

void av_free(void *ptr)

Gibt den mit av_malloc () oder av_realloc () zugewiesenen Speicher frei. Sie können diese Funktion mit ptr == NULL aufrufen. Es wird empfohlen, stattdessen av_freep () aufzurufen.

void av_freep(void *ptr)

Gibt Speicher frei und setzt den Zeiger auf NULL. Verwendet intern av_free ().

void av_free_packet(AVPacket *pkt)

Wrap um die Paketzerstörungsmethode (pkt-> destruct).

int64_t av_gettime()

Holen Sie sich die aktuelle Zeit in Mikrosekunden.

void av_init_packet(AVPacket *pkt)

Initialisieren optionaler Paketfelder.

void *av_malloc(unsigned int size)

Byte-Größe der Speicherzuordnung mit Ausrichtung, die für alle Speicherzugriffe geeignet ist (einschließlich Vektoren, falls auf der CPU verfügbar). av_malloc (0) sollte einen Zeiger ungleich Null zurückgeben.

void *av_mallocz(unsigned int size)

Entspricht av_malloc (), initialisiert jedoch den Speicher auf Null.

double av_q2d(AVRational a)

Verdoppelt AVRational.

int av_read_frame(AVFormatContext *s, AVPacket *pkt)

Gibt den nächsten Stream-Frame zurück. Informationen werden als Paket in pkt gespeichert.

Das zurückgegebene Paket ist bis zum nächsten av_read_frame () oder bis av_close_input_file () gültig und muss mit av_free_packet freigegeben werden. Denn ein Videopaket enthält genau einen Frame. Für Audio enthält es eine ganzzahlige Anzahl von Frames, wenn jeder Frame eine bekannte feste Größe hat (z. B. PCM- oder ADPCM-Daten). Wenn die Audio-Frames eine variable Größe haben (z. B. MPEG-Audio), enthält sie einen Frame.

pkt-> pts, pkt-> dts und pkt-> duration werden immer auf die richtigen Werte in Einheiten von AVStream.timebase gesetzt (und es wird angenommen, dass das Format sie nicht bereitstellen kann). pkt-> pts kann AV_NOPTS_VALUE sein, wenn das Videoformat B-Frames enthält. Wenn Sie die Nutzdaten nicht entpacken, ist es daher besser, sich auf pkt-> dts zu verlassen.

Zurückgegebenes Ergebnis: 0, wenn alles in Ordnung ist, <0, wenn ein Fehler vorliegt oder das Ende der Datei.

void av_register_all();

Registriert alle Codecs in der Bibliothek.

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

Gibt a * bq / cq zurück .

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

Sucht zu einem Zeitstempel nach einem Keyframe.

stream_index : Wenn stream_index -1 ist, wird der Standard-Stream ausgewählt und der Zeitstempel wird automatisch von AV_TIME_BASE-Einheiten in eine streamspezifische Zeitbasis konvertiert.
Zeitstempel : Zeitstempel gemessen in Einheiten von AVStream.time_base oder, wenn kein Stream angegeben ist, in Einheiten von AV_TIME_BASE.
Flags :
Legen Sie die Parameter für die Richtung und den Suchmodus fest: AVSEEK_FLAG_ANY: Suchen Sie in einem beliebigen Frame, nicht nur in den wichtigsten.
AVSEEK_FLAG_BACKWARD: Suche in die entgegengesetzte Richtung.
AVSEEK_FLAG_BYTE: Suche basierend auf der Position in Bytes.

AVFrame *avcodec_alloc_frame()

Alter Name für av_frame_alloc. Geändert in lavc 55.28.1.

AVFrame *av_frame_alloc()

Wählt einen AVFrame aus und initialisiert ihn. Kann mit av_frame_free () freigegeben werden.

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

Dekodiert ein Audio-Frame von avpkt zu Frame. Die Funktion avcodec_decode_audio4 () decodiert eine Audiodatei von AVPacket. Für die Dekodierung wird ein Audio-Codec verwendet, der mit avctx über avcodec_open2 () verknüpft wurde. Der resultierende decodierte Frame wird im angegebenen AVFrame gespeichert. Wenn der Frame entpackt wurde, wird got_frame_ptr auf 1 gesetzt.

Warnung: Der Eingabepuffer avpkt-> data muss FF_INPUT_BUFFER_PADDING_SIZE größer sein als die tatsächlichen Lesebytes, da einige optimierte Bitstream-Lesegeräte jeweils 32 oder 64 Bit lesen und bis zu lesen können das Ende.

avctx : Codec-Kontext.
Frame : Zielframe.
got_frame_ptr : Ziel-Int, das festgelegt wird, wenn der Frame entpackt wurde.
AVPKT: AVPacket mit Audio.

Zurückgegebenes Ergebnis: Wenn ein Fehler zurückgegeben wird, wird ein negativer Wert zurückgegeben, andernfalls wird die Anzahl der vom Eingabe-AVPacket verwendeten Bytes zurückgegeben.

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

Dekodiert ein Videobild von buf in ein Bild. Die Funktion avcodec_decode_video2 () decodiert einen Videorahmen aus einem Eingabepuffer der Größe buf_size. Für die Dekodierung wird ein Videocodec verwendet, der mit avctx über avcodec_open2 () verknüpft wurde. Der resultierende dekodierte Rahmen wird im Bild gespeichert.

Warnung: Ausrichtungsbeispiele und Pufferprobleme, die für avcodec_decode_audio4 gelten, gelten auch für diese Funktion.

avctx : Codec-Kontext.
Bild : AVFrame, in dem das dekodierte Video gespeichert wird.
frameFinished : Null, wenn keine Frames entpackt werden können, andernfalls ist es nicht gleich Null.
avpkt: AVPacket mit Eingangspuffer eingeben. Sie können ein solches Paket mit av_init_packet () erstellen. Bei angegebenen Daten und Größen benötigen einige Decoder möglicherweise zusätzlich andere Felder, z. B. Flags und AV_PKT_FLAG_KEY. Alle Decoder sind so konzipiert, dass möglichst wenige Felder verwendet werden.

Zurückgegebenes Ergebnis: Bei einem Fehler wird ein negativer Wert zurückgegeben, andernfalls wird die Anzahl der Bytes verwendet oder Null, wenn keine Frames dekomprimiert werden können.

int64_t av_frame_get_best_effort_timestamp (const AVFrame *frame)

Eine einfache Zugriffsmethode, um best_effort_timestamp von einem AVFrame-Objekt abzurufen.

AVCodec *avcodec_find_decoder(enum CodecID id)

Sucht nach einem Decoder mit CodecID. Gibt bei einem Fehler NULL zurück. Es sollte aufgerufen werden, nachdem der erforderliche AVCodecContext aus dem Stream in AVFormatContext mit codecCtx-> codec_id abgerufen wurde.

void avcodec_flush_buffers(AVCodecContetx *avctx)

Pufferspülung. Wird beim Suchen oder Wechseln zu einem anderen Stream aufgerufen.

AVCodecContext * avcodec_alloc_context3 (const AVCodec *codec)

Weist AVCodecContext zu und setzt seine Felder auf Standardwerte.

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

Kopieren Sie die Einstellungen des Quell-AVCodecContext in den Ziel-AVCodecContext. Der resultierende Kontext des Zielcodecs wird geschlossen, d.h. Sie müssen avcodec_open2 () aufrufen, bevor Sie diesen AVCodecContext zum Decodieren / Codieren von Video- / Audiodaten verwenden.

dest : muss mit avcodec_alloc_context3 (NULL) initialisiert werden, sonst wird es nicht initialisiert.

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

Initialisiert avctx so, dass der im Codec angegebene Codec verwendet wird . Sollte nach avcodec_find_decoder verwendet werden. Gibt bei Erfolg Null und bei Fehler einen negativen Wert zurück.

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

Legt die Struktur fest, auf die das Bild zeigt, mit dem ptr- Puffer , dem pix_fmt- Format und der angegebenen Breite und Höhe. Gibt die Größe der Bilddaten in Bytes zurück.

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

Berechnet, wie viele Bytes für ein Bild mit einer bestimmten Breite, Höhe und einem bestimmten Bildformat benötigt werden.

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

Gibt SwsContext zur Verwendung in sws_scale zurück.

srcW , srcH , srcFormat : Breite, Höhe und Format der gewünschten Pixel.
dstW , dstH , dstFormat : Breite, Höhe und Format der endgültigen Pixel.
Flags : Die zu verwendende Skalierungsmethode.
Folgende Optionen stehen zur Verfügung: SWS_FAST_BILINEAR, SWS_BILINEAR, SWS_BICUBIC, SWS_X, SWS_POINT, SWS_AREA, SWS_BICUBLIN, SWS_GAUSS, SWS_SINC, SWS_LANCZOS, SWS_SPLINE.
Andere Flags umfassen CPU-Fähigkeitsflags: SWS_CPU_CAPS_MMX, SWS_CPU_CAPS_MMX2, SWS_CPU_CAPS_3DNOW, SWS_CPU_CAPS_ALTIVEC.
Andere Flags sind (derzeit nicht vollständig implementiert) SWS_FULL_CHR_H_INT, SWS_FULL_CHR_H_INP und SWS_DIRECT_BGR.
Schließlich gibt es SWS_ACCURATE_RND und vielleicht das nützlichste für Anfänger, SWS_PRINT_INFO.
Ich habe keine Ahnung, was die meisten von ihnen tun. Vielleicht an mich schreiben?
srcFilter , dstFilter : SwsFilter für Quelle und Ziel. SwsFilter aktiviert die Farb- / Helligkeitsfilterung. Der Standardwert ist NULL.
param : sollte ein Zeiger auf einen int [2] -Puffer mit Koeffizienten sein. Nicht dokumentiert. Es scheint verwendet zu werden, um Standard-Skalierungsalgorithmen leicht zu modifizieren. Der Standardwert ist NULL. Nur für Experten!

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

Skaliert Daten in src gemäß unseren Einstellungen in unserem SwsContext * c .
srcStride und dstStride sind die Quell- und Zielzeilengrößen .

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

Fügt eine Rückruffunktion hinzu, die nach einer bestimmten Anzahl von Millisekunden ausgeführt wird. Die Rückruffunktion übergibt das aktuelle Zeitintervall und den vom Benutzer angegebenen Parameter aus dem Aufruf SDL_AddTimer und gibt das nächste Zeitintervall zurück. (Wenn der Rückgabewert des Rückrufs mit dem übergebenen Wert übereinstimmt, arbeitet der Timer mit der gleichen Geschwindigkeit weiter.) Wenn der Rückgabewert des Rückrufs 0 ist, wird der Timer abgebrochen.
Eine andere Möglichkeit, den aktuellen Timer abzubrechen, besteht darin, SDL_RemoveTimer mit der Timer-ID aufzurufen (die von SDL_AddTimer zurückgegeben wurde).

Die Timer-Rückruffunktion wird möglicherweise in einem anderen Thread als Ihrem Hauptprogramm ausgeführt und sollte daher keine Funktionen von sich aus aufrufen. Sie können jedoch jederzeit SDL_PushEvent aufrufen.

Der Detaillierungsgrad des Timers hängt von der Plattform ab. Sie müssen jedoch mit mindestens 10 ms rechnen, da dies der häufigste Wert ist. Dies bedeutet, dass der Rückruf auf einem entladenen System nach ca. 20 ms startet, wenn Sie einen 16-ms-Timer anfordern. Wenn Sie ein Flag setzen müssen, das die Aktualisierung von Frames mit einer Geschwindigkeit von 30 Frames pro Sekunde (alle 33 ms) signalisiert, können Sie einen Timer für 30 ms setzen (siehe Beispiel unten). Wenn Sie diese Funktion verwenden, müssen Sie SDL_INIT_TIMER an SDL_Init übergeben.

Gibt den Bezeichnerwert für den hinzugefügten Timer oder NULL zurück, wenn ein Fehler auftritt.

Format für Rückruf:
Uint32 callback ( Uint32, void * param)


int SDL_CondSignal(SDL_cond *cond)

Neustarten eines der Gewinde für die Warte cond Bedingungsvariable . Gibt bei Erfolg 0 und bei Fehler -1 zurück.

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

Entsperren Sie den bereitgestellten Mutex und warten Sie, bis ein anderer Thread SDL_CondSignal oder SDL_CondBroadcast für die Bedingungsvariable cond aufruft. Sperren Sie dann den Mutex erneut. Der Mutex muss vor dem Aufrufen dieser Funktion gesperrt sein. Gibt 0 zurück, wenn ein Signal empfangen wird, oder -1 bei einem Fehler.

SDL_cond *SDL_CreateCond(void);

Erstellt eine Bedingungsvariable.

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

SDL_CreateThread erstellt einen neuen Ausführungsthread, der den gesamten globalen Speicher des übergeordneten Elements, der Signalhandler, der Dateideskriptoren usw. gemeinsam nutzt. Und es führt die Funktion fn aus und übergibt ihr die ungültigen Zeigerdaten. Der Thread wird beendet, wenn fn einen Wert zurückgibt.

void SDL_Delay (Uint32 );

Wartet auf die angegebene Anzahl von Millisekunden. SDL_Delay wartet mindestens die angegebene Zeit, möglicherweise jedoch aufgrund der Betriebssystemplanung länger.
Hinweis: Erwarten Sie eine Verzögerungsgranularität von mindestens 10 ms. Einige Plattformen haben kürzere Maßnahmen, dies ist jedoch die häufigste Option.

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

SDL_CreateYUVOverlay erstellt eine YUV-Überlagerung mit der angegebenen Breite, Höhe und dem angegebenen Format (eine Liste der verfügbaren Formate finden Sie in der SDL_Overlay-Datenstruktur) für die bereitgestellte Anzeige. Gibt SDL_Overlay zurück.

Die Anzeige sollte eigentlich eine von SDL_SetVideoMode abgeleitete Oberfläche sein, andernfalls funktioniert diese Funktion standardmäßig.

Der Begriff „Überlagerung“ ist falsch, da der Inhalt der Anzeigefläche unterhalb des Bereichs, in dem die Überlagerung angezeigt wird, überschrieben wird, wenn die Überlagerung angezeigt wird, wenn die Überlagerung nicht in Hardware erstellt wird.

int SDL_LockYUVOverlay(SDL_Overlay *overlay)

SDL_LockYUVOverlay blockiert die Überlagerung für den direkten Zugriff auf Pixeldaten. Gibt bei Erfolg 0 oder bei Fehler -1 zurück.

void SDL_UnlockYUVOverlay(SDL_Overlay *overlay)

Schaltet eine zuvor gesperrte Überlagerung frei. Das Overlay muss entsperrt sein, bevor es angezeigt werden kann.

int SDL_DisplayYUVOverlay(SDL_Overlay *overlay, SDL_Rect *dstrect)

Setzt die Überlagerung auf die Oberfläche, die beim Erstellen angegeben wurde. Die Struktur SDL_Rect dstrect definiert die Position und Größe des Ziels. Wenn dstrect mehr oder weniger Overlay ist, wird das Overlay skaliert. Dies ist für die zweifache Skalierung optimiert. Gibt bei Erfolg 0 zurück.

void SDL_FreeYUVOverlay(SDL_Overlay *overlay)

Gibt das von SDL_CreateYUVOverlay erstellte Overlay frei.

int SDL_Init(Uint32 flags);

Initialisiert die SDL. Dies sollte vor allen anderen SDL-Funktionen aufgerufen werden. Der Parameter flags gibt an, welche Teile der SDL initialisiert werden sollen.

SDL_INIT_TIMER - Initialisiert das Timer-Subsystem.
SDL_INIT_AUDIO - Initialisiert das Audio-Subsystem.
SDL_INIT_VIDEO - Initialisiert das Video-Subsystem.
SDL_INIT_CDROM - Initialisiert das CD-ROM-Subsystem.
SDL_INIT_JOYSTICK - Initialisiert das Joystick-Subsystem.
SDL_INIT_EVERYTHING - Initialisiert alle oben genannten Elemente.
SDL_INIT_NOPARACHUTE - SDL kann keine schwerwiegenden Fehler abfangen.
SDL_INIT_EVENTTHREAD - Startet den Event Manager in einem separaten Thread.

Gibt bei Fehler -1 oder bei Erfolg 0 zurück. Sie können eine erweiterte Fehlermeldung erhalten, indem Sie SDL_GetError aufrufen. Eine typische Fehlerursache ist die Verwendung einer bestimmten Anzeige ohne die entsprechende Unterstützung für das Subsystem, z. B. das Fehlen eines Maustreibers bei Verwendung eines Bildpuffers mit dem Gerät. In diesem Fall können Sie SDL entweder ohne Maus kompilieren oder die Umgebungsvariable "SDL_NOMOUSE = 1" festlegen, bevor Sie die Anwendung starten.

SDL_mutex *SDL_CreateMutex(void);

Erstellt einen neuen, freigeschalteten Mutex.

int SDL_LockMutex(SDL_mutex *mutex)

SDL_LockMutex ist ein Alias ​​für SDL_mutexP. Es blockiert einen Mutex, der zuvor mit SDL_CreateMutex erstellt wurde. Wenn der Mutex bereits von einem anderen Thread blockiert ist, gibt SDL_mutexP keinen Wert zurück, bis der von ihm blockierte Thread ihn entsperrt (mithilfe von SDL_mutexV). Wenn der Mutex erneut aufgerufen wird, muss SDL_mutexV (auch bekannt als SDL_UnlockMutex) gleich oft aufgerufen werden, um den Mutex in einen entsperrten Zustand zurückzusetzen. Gibt bei Erfolg 0 oder bei Fehler -1 zurück.

int SDL_UnlockMutex(SDL_Mutex *mutex)

Mutex entsperren.

int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)

Diese Funktion öffnet die Audioeinheit mit den erforderlichen Parametern und gibt bei Erfolg 0 zurück, wobei die tatsächlichen Hardwareparameter in der Struktur platziert werden, auf die sie letztendlich zeigt. Wenn ein NULL-Wert empfangen wird, haben die an die Rückruffunktion übergebenen Audiodaten garantiert das erforderliche Format und werden bei Bedarf automatisch in das Hardware-Audioformat konvertiert. Diese Funktion gibt -1 zurück, wenn das Audiogerät nicht geöffnet oder der Audiostream nicht konfiguriert werden konnte.

Um ein Audiogerät zu öffnen, müssen Sie die gewünschte SDL_AudioSpec erstellen. Dann müssen Sie diese Struktur mit den gewünschten Audiospezifikationen füllen.

gewünschte-> Frequenz : gewünschte Schallfrequenz in Samples pro Sekunde.
gewünschtes-> Format : gewünschtes Audioformat (siehe SDL_AudioSpec).
gewünschte-> Kanäle: Erforderliche Kanäle (1 für Mono, 2 für Stereo, 4 für Surround-Sound, 6 für Surround-Sound mit Zentrierung und LFE).
gewünschte-> Samples : gewünschte Audiopuffergröße in Samples. Diese Zahl muss eine Zweierpotenz sein und kann vom Audiotreiber auf einen für die Hardware besser geeigneten Wert eingestellt werden. Die optimalen Werte reichen von 512 bis einschließlich 8192, abhängig von der Anwendung und der Prozessorgeschwindigkeit. Kleinere Werte führen zu schnelleren Antwortzeiten, können jedoch zu einer schlechten Leistung führen, wenn die Anwendung eine umfangreiche Verarbeitung durchführt und den Audiopuffer nicht rechtzeitig füllen kann. Das Stereo-Sample besteht aus dem rechten und dem linken Kanal in LR-Reihenfolge. Bitte beachten Sie, dass die Anzahl der Proben mit der folgenden Formel direkt mit der Zeit zusammenhängt: ms = (Proben * 1000) / Freq .
gewünscht-> Rückruf : muss auf eine Funktion eingestellt sein, die aufgerufen wird, wenn die Audioeinheit bereit ist, zusätzliche Daten zu empfangen. Der Zeiger auf den Audiopuffer und die Länge des Audiopuffers in Bytes werden übertragen. Diese Funktion wird normalerweise in einem separaten Thread ausgeführt. Daher müssen die Datenstrukturen, auf die zugegriffen wird, durch Aufrufen von SDL_LockAudio und SDL_UnlockAudio im Code geschützt werden. Der Callback-Prototyp ist void callback ( void * userdata , Uint8 * stream , int len ) . Benutzerdaten - Ein Zeiger, der im Benutzerdatenfeld SDL_AudioSpec gespeichert ist. StromIst ein Zeiger auf den Audiopuffer, den Sie mit Informationen füllen möchten, und len ist die Länge des Audiopuffers in Bytes.
Erforderlich-> Benutzerdaten : Dieser Zeiger wird als erster Parameter an die Rückruffunktion übergeben.

SDL_OpenAudio liest diese Felder aus der gewünschten SDL_AudioSpec-Struktur, die an die Funktion übergeben wurde, und versucht, die Audiokonfiguration zu finden, die Ihrem Wunsch entspricht. Wie oben erwähnt, wird SDL während der Wiedergabe von den gewünschten Soundeinstellungen in Geräteeinstellungen konvertiert, wenn der resultierende Parameter NULL ist.

Wenn NULL zurückgegeben wird, ist die erforderliche SDL_AudioSpec Ihre Arbeitsspezifikation, andernfalls wird die resultierende SDL_AudioSpec zu einer Arbeitsspezifikation, und die gewünschte Spezifikation kann gelöscht werden. Die Daten in der Arbeitsspezifikation werden beim Erstellen von SDL_AudioCVT verwendet, um die heruntergeladenen Daten in das Geräteformat zu konvertieren.

SDL_OpenAudio berechnet Größen- und Stillefelder sowohl für die gewünschte als auch für die resultierende Spezifikation. Das Größenfeld speichert die Gesamtgröße des Audiopuffers in Bytes, während die Stille den Wert speichert, der zur Darstellung der Stille im Audiopuffer verwendet wird

Das Audiogerät beginnt mit der Wiedergabe der Stille, wenn es geöffnet ist, und sollte für die Wiedergabe durch Aufrufen von SDL_PauseAudio (0) eingeschaltet werden, wenn Sie bereit sind, die Audio-Rückruffunktion aufzurufen. Da der Audiotreiber die angeforderte Größe des Audiopuffers ändern kann, müssen Sie nach dem Öffnen des Audiogeräts alle lokalen Mischpuffer auswählen.

void SDL_PauseAudio(int pause_on)

Diese Funktion pausiert und stoppt die Audio-Rückrufverarbeitung. Es muss nach dem Öffnen des Audiogeräts mit pause_on = 0 aufgerufen werden, um die Tonwiedergabe zu starten. Auf diese Weise können Sie die Daten für die Rückruffunktion nach dem Öffnen der Audioeinheit sicher initialisieren. Während einer Pause wird die Stille auf dem Audiogerät aufgezeichnet.

int SDL_PushEvent(SDL_Event *event)

Eine Ereigniswarteschlange, die tatsächlich als bidirektionaler Kommunikationskanal verwendet wird. Es können nicht nur Ereignisse aus der Warteschlange gelesen werden, sondern der Benutzer kann auch seine eigenen Ereignisse in die Warteschlange stellen. Ein Ereignis ist ein Zeiger auf die Struktur des Ereignisses, das Sie in die Warteschlange stellen möchten. Das Ereignis wird in die Warteschlange kopiert, und der Aufrufer kann den Speicher verwalten, auf den nach der Rückgabe von SDL_PushEvent verwiesen wird. Diese Funktion ist threadorientiert und kann sicher von anderen Threads aufgerufen werden. Gibt 0 zurück, wenn erfolgreich, oder -1, wenn das Ereignis nicht ausgelöst werden konnte.

int SDL_WaitEvent(SDL_Event *event)

Wartet auf unbestimmte Zeit auf das nächste verfügbare Ereignis und gibt 0 zurück, wenn beim Warten auf Ereignisse ein Fehler aufgetreten ist, andernfalls 1. Wenn das Ereignis nicht NULL ist, wird das nächste Ereignis aus der Warteschlange entfernt und in diesem Bereich gespeichert.

void SDL_Quit()

Deaktiviert alle SDL-Subsysteme und gibt die ihm zugewiesenen Ressourcen frei. Dies sollte immer vor dem Beenden aufgerufen werden.

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

Videomoduseinstellung mit den angegebenen Breiten-, Höhen- und Pixelbits. Ab SDL 1.2.10 werden bei einer Breite und Höhe von 0 die Breite und Höhe des aktuellen Videomodus (oder des Desktop-Modus, wenn der Modus nicht eingestellt ist) verwendet. Wenn Bitsperpixel 0 ist, wird es als die aktuellen Anzeigebits pro Pixel behandelt. Der Parameter flags ist derselbe wie das Feld flags der Struktur SDL_Surface. Oder eine Kombination der folgenden Werte:

SDL_SWSURFACE - Erstellen Sie eine Videooberfläche im Systemspeicher.
SDL_HWSURFACE - Erstellt eine Videooberfläche im Videospeicher.
SDL_ASYNCBLIT - Aktivieren Sie die Verwendung von asynchronen Aktualisierungen der Anzeigeoberfläche. Dies verlangsamt normalerweise die Arbeit auf Einzelprozessor-Computern, kann jedoch die Geschwindigkeit in SMP-Systemen erhöhen.
SDL_ANYFORMAT - Wenn normalerweise eine Videooberfläche mit den angeforderten Bits pro Pixel (bpp - von Bits pro Pixel) nicht verfügbar ist, emuliert SDL Videos mit einer schattierten Oberfläche. Das Übergeben von SDL_ANYFORMAT verhindert dies und zwingt die SDL, die Oberfläche des Videos unabhängig von seiner Pixeltiefe zu verwenden.
SDL_HWPALETTE - Bietet exklusiven SDL-Zugriff auf die Palette. Ohne dieses Flag können Sie mit SDL_SetColors oder SDL_SetPalette nicht immer die gewünschten Farben abrufen.
SDL_DOUBLEBUF - Hardware-Doppelpufferung aktivieren; Nur gültig mit SDL_HWSURFACE. Ein Aufruf von SDL_Flip kehrt die Puffer um und aktualisiert den Bildschirm. Alle Zeichnungen finden auf einer Oberfläche statt, die derzeit nicht angezeigt wird. Wenn die doppelte Pufferung nicht aktiviert werden kann, führt SDL_Flip einfach SDL_UpdateRect im Vollbildmodus aus.
SDL_FULLSCREEN SDL - Versuchen Sie, den Vollbildmodus zu verwenden. Wenn eine Änderung der Hardwareauflösung (aus irgendeinem Grund) nicht möglich ist, wird die nächsthöhere Auflösung verwendet und das Anzeigefenster wird auf einem schwarzen Hintergrund zentriert.
SDL_OPENGL - Erstellt einen OpenGL-Rendering-Kontext. Es wird davon ausgegangen, dass die OpenGL-Videoattribute mit SDL_GL_SetAttribute voreingestellt sind.
SDL_OPENGLBLIT - Erstellen Sie einen OpenGL-Rendering-Kontext wie oben beschrieben, lassen Sie jedoch normale Blitting-Vorgänge zu. Eine Bildschirmoberfläche (2D) kann einen Alphakanal haben, und SDL_UpdateRects sollte verwendet werden, um Änderungen der Bildschirmoberfläche zu aktualisieren. HINWEIS. Diese Option wird nur aus Kompatibilitätsgründen gespeichert und in zukünftigen Versionen entfernt. Nicht für die Verwendung in neuem Code empfohlen.
SDL_RESIZABL - Erstellt ein Fenster mit veränderbarer Größe. Wenn die Fenstergröße vom Benutzer geändert wird, wird das Ereignis SDL_VIDEORESIZE generiert und SDL_SetVideoMode kann erneut mit einer neuen Größe aufgerufen werden.
SDL_NOFRAME Wenn möglich, zwingt SDL_NOFRAME SDL, ein Fenster ohne Titel oder Rahmen zu erstellen. Dieses Flag wird automatisch im Vollbildmodus gesetzt.
Hinweis. Unabhängig davon, welche SDL_SetVideoMode-Flags erfüllen können, werden sie im flags-Element der zurückgegebenen Oberfläche gesetzt.
HINWEIS. Das Bitpixel 24 verwendet eine gepackte Darstellung von 3 Bytes pro Pixel. Verwenden Sie für den gängigeren 4-Byte-pro-Pixel-Modus 32-Bit-Pixel. Seltsamerweise fordern sowohl 15 als auch 16 einen 2-Byte-pro-Pixel-Modus an, jedoch mit unterschiedlichen Pixelformaten.
HINWEIS. Verwenden Sie SDL_SWSURFACE, wenn Sie separate Pixelmanipulationen durchführen oder Oberflächen mithilfe von Alphakanälen ziehen möchten und eine hohe Bildrate benötigen. Wenn Sie Hardwareoberflächen (SDL_HWSURFACE) verwenden, kopiert die SDL Oberflächen aus dem Videospeicher in den Systemspeicher, wenn Sie sie sperren, und umgekehrt, wenn Sie sie entsperren. Dies kann zu einer deutlichen Leistungsminderung führen. (Beachten Sie, dass Sie nach einer Hardwareoberfläche fragen können, aber dennoch eine Softwareoberfläche erhalten. Viele Plattformen können nur bei Verwendung von SDL_FULLSCREEN eine Hardwareoberfläche bereitstellen.) SDL_HWSURFACE wird am besten verwendet, wenn die Oberflächen, die Sie blotten, auch im Videospeicher gespeichert werden können.
HINWEIS. Wenn Sie die Position auf dem Bildschirm beim Erstellen der Fensteroberfläche steuern möchten, können Sie dazu die Umgebungsvariablen "SDL_VIDEO_CENTERED = center" oder "SDL_VIDEO_WINDOW_POS = x, y" festlegen. Sie können sie über SDL_putenv installieren.

Rückgabewert: Frame Buffer Surface oder NULL im Fehlerfall. Die zurückgegebene Oberfläche wird von SDL_Quit freigegeben und darf vom Aufrufer nicht freigegeben werden.
HINWEIS. Diese Regel umfasst aufeinanderfolgende Aufrufe von SDL_SetVideoMode (d. H. Größenänderung) - die vorhandene Oberfläche wird automatisch freigegeben.






Anhang 2. Datenstrukturen



AVCodecContext

Alle Informationen zum Codec aus dem Stream, von AVStream-> Codec. Einige wichtige Attribute:

AVRational time_base : Anzahl der Frames pro Sekunde
int sample_rate : Samples pro Sekunde
int channel : Anzahl der Kanäle

Die vollständige Liste (sehr beeindruckend) finden Sie hier ( Webarchiv, da der ursprüngliche Link bereits nicht vorhanden ist ). Viele Parameter werden hauptsächlich zum Codieren verwendet, nicht zum Decodieren.

AVFormatContext

Datenfelder:

const AVClass * av_class
AVInputFormat * iformat
AVOutputFormat * oformat
Leere * priv_data :
ByteIOContext pb : für Low-Level - Datei Manipulation.
unsigned int nb_streams : Anzahl der Threads in der Datei.
AVStream * -Streams [MAX_STREAMS] : Hier werden Daten für jeden Stream gespeichert.
char Dateiname [1024]: aber was ist ohne ihn (im Original - duh ). Dateiinformationen

:
int64_t Zeitstempel :
char title [512]:
char author [512]:
char copyright [512]:
char comment [512]:
char album [512]:
int year :
int track :
char genre [32]:

int ctx_flags :
Mögliche Werte sind AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS, AVFMT_RAWPICTURE, AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_GENERIC_INDEX
AVPacketList * packet_buffer : Dieser Puffer wird nur benötigt, wenn Pakete bereits gepuffert, aber nicht decodiert sind, um beispielsweise Codec-Parameter in MPEG-Streams zu empfangen.
int64_tstart_time : beim Decodieren: Die Position des ersten Frames der Komponente in Bruchteilen einer Sekunde, AV_TIME_BASE. Stellen Sie diesen Wert NIEMALS direkt ein: Er wird aus den Werten von AVStream abgeleitet.
int64_t Dauer: Dekodierung : Dauer des Streams in Bruchteilen von AV_TIME_BASE. Stellen Sie diesen Wert NIEMALS direkt ein: Er wird aus den Werten von AVStream abgeleitet.
int64_t file_size : Gesamtgröße der Datei, 0, falls unbekannt.
int bit_rate : decoding: Gesamtbitrate des Streams in Bit / s, 0, falls nicht verfügbar. Stellen Sie es NIEMALS direkt ein, wenn file_size und die in ffmpeg bekannte Dauer es automatisch berechnen können.
AVStream * cur_st
const uint8_t * cur_ptr
int cur_len
AVPacket cur_pkt :
int64_t data_offset :
int index_built : Offset des ersten Pakets.
mux_rate int :
int packet_size :
int Vorbelastung :
int max_delay :
int loop_output : Anzahl der Ausgangsschleifen in den unterstützten Formaten.
int flags :
int loop_input :
unsigned int probesize : decoding: Beispieldatengröße; wird bei der Codierung nicht verwendet.
int max_analyze_duration : maximale Dauer in Einheiten von AV_TIME_BASE, während der die Eingabedaten in av_find_stream_info () analysiert werden müssen
const uint8_t * key :
int keylen :

AVIOContext

E / A-Kontext für den Zugriff auf Ressourcen.

const AVClass * av_class : Klasse für private Einstellungen.
vorzeichenloser char * buffer : Beginn des Puffers.
int buffer_size : maximale Puffergröße.
unsigned char * buf_ptr : aktuelle Position im Puffer.
unsigned char * buf_end : Daten können kleiner als buffer + buffer_size sein, wenn die Lesefunktion beispielsweise weniger Daten als angefordert zurückgibt.
void * opaque : privater Zeiger an read / write / search / ...
int (* read_packet) übergeben (void * opaque, uint8_t * buf, int buf_size) :
int (* write_packet) (void * opaque, uint8_t * buf, int buf_size ) :
int64_t (* seek) (void * undurchsichtig, int64_t offset, int
wherece ) : int64_t pos : Position in der Datei des aktuellen Puffers.
int must_flush : true, wenn die nächste Suche zurückgesetzt werden soll.
int eof_reached : true, wenn das Dateiende erreicht ist.
int write_flag : true, wenn zum Schreiben geöffnet.
int max_packet_size :
vorzeichenlose lange Prüfsumme :
vorzeichenlose char * Prüfsumme_ptr :
vorzeichenlose lange Prüfsumme (* update_checksum) (vorzeichenlose lange Prüfsumme, const uint8_t * buf, vorzeichenlose int-Größe) :
int error : enthält den Fehlercode oder 0, wenn kein Fehler aufgetreten ist.
int (* read_pause) (void * undurchsichtig, int pause): Unterbrechen oder setzen Sie beispielsweise die Wiedergabe für Netzwerk-Streaming-Protokolle fort.
int64_t (* read_seek) (void * opaque, int stream_index, int64_t timestamp, int flags) : Suche nach dem angegebenen Zeitstempel im Stream mit dem angegebenen stream_index-Index.
int seekable : Kombination der Flags AVIO_SEEKABLE_ oder 0, wenn der Stream nicht durchsuchbar ist.
int64_t maxsize : Maximale Dateigröße, mit der die Auswahl eingeschränkt wird. Dieses Feld ist für libavformat intern und der Zugriff von außen ist verboten.
int direct : avio_read und avio_write sollten nach Möglichkeit direkt ausgeführt werden und nicht den Puffer durchlaufen, und avio_seek ruft immer direkt die Hauptsuchfunktion auf.
int64_t bytes_read: Byte Read Statistics Dieses Feld ist intern für libavformat und der externe Zugriff wird verweigert.
int seek_count : Suchstatistik . Dieses Feld ist für libavformat intern und der Zugriff von außen ist verboten.
int writeout_count : Statistik schreiben. Dieses Feld ist für libavformat intern und der Zugriff von außen ist verboten.
int orig_buffer_size : Die ursprüngliche Puffergröße, die intern verwendet wird, nachdem die Puffergröße überprüft und zurückgegeben wurde. Dieses Feld ist für libavformat intern und der Zugriff von außen ist verboten.

AVDictionary

Wird verwendet, um Parameter an ffmpeg zu übergeben.

int count :
AVDictionaryEntry * elems :

AVDictionaryEntry

Wird zum Speichern von Wörterbucheinträgen in AVDictionary verwendet.

char * ket :
char * value :

AVFrame

Diese Struktur hängt von der Art des Codecs ab und wird daher dynamisch bestimmt. Es gibt jedoch allgemeine Eigenschaften und Methoden für diese Struktur:

uint8_t * data [4] :
int linesize [4] : information stride.
uint8_t * base [4] :
int key_frame :
int pict_type :
int64_t pts : Dies sind nicht die Punkte, die Sie beim Decodieren erwarten.
int coded_picture_number :
int display_picture_number :
int Qualität :
int Alter :
int Referenz :
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 * undurchsichtig : Benutzerdaten
uint64_t Fehler [4] :
int type :
int repeat_pict : Weist Sie an, das Bild eine bestimmte Anzahl von Malen zu wiederholen.
int qscale_type :
int interlaced_frame :
int top_field_first :
AVPanScan * pan_scan :
int palette_has_changed :
int buffer_hints :
short * dct_coeff :
int8_t * ref_index [2] :

AVPacket

Die Struktur, in der die Rohpaketdaten gespeichert werden. Diese Daten müssen an avcodec_decode_audio2 oder avcodec_decode_video übertragen werden, um einen Frame zu erhalten.

int64_t pts : Präsentationszeitstempel in time_base-Einheiten.
int64_t dts : Zeitstempel der Dekomprimierung in Einheiten der Zeitbasis.
uint8_t * data : Rohdaten.
int size : Datengröße.
int stream_index : Stream, aus dem AVPacket stammt, basierend auf der Menge in AVFormatContext.
int flags : PKT_FLAG_KEY wird gesetzt, wenn das Paket ein Schlüsselbild ist.
int duration : Dauer der Präsentation in Einheiten von time_base (0, falls nicht verfügbar)
void (* destruct) (struct AVPacket *) : Ressourcenfreigabefunktion für dieses Paket (standardmäßig av_destruct_packet).
void * priv :
int64_t pos : Byte-Position im Stream, -1, falls unbekannt.

AVPacketList

Eine einfache verknüpfte Liste für Pakete.

AVPacket pkt :
AVPacketList * next :

AVPicture

Diese Struktur entspricht genau den ersten beiden AVFrame-Datenelementen und wird daher häufig verworfen. Wird häufig in SWS-Funktionen verwendet.

uint8_t * data [4] :
int linesize [4] : Die Anzahl der Bytes in der Zeichenfolge.

AVRational

Einfache Struktur zur Darstellung rationaler Zahlen.

int num : Zähler.
int den : Nenner.

AVStream

Die Struktur für den Stream. Sie werden diese Informationen wahrscheinlich am häufigsten im Codec verwenden.

int index :
int id :
AVCodecContext * Codec :
AVRational r_frame_rate :
void * priv_data :
int64_t codec_info_duration :
int codec_info_nb_frames :
AVFrac pts :
AVRational time_base :
int pts_wrap_bits :
int stream_copy :
eine Enumeration AVDiscard den Ablage : Sie können die Pakete auswählen , die weggeworfen werden, weil sie nicht brauchen beim Demultiplexen.
Float-Qualität :
int64_t Startzeit :
int64_t Dauer:
Char Sprache [4] :
int need_parsing : 1 -> müssen voll Parsing, 2 -> Parse - Header nur ohne Umpacken
AVCodecParserContext * Parser :
int64_t cur_dts :
int last_IP_duration :
int64_t last_IP_pts :
AVIndexEntry * index_entries :
int nb_index_entries :
unsigned int index_entries_allocated_size :
int64_t nb_frames : Anzahl der Frames in diesem Stream (falls bekannt) oder 0
int64_t pts_buffer [MAX_REORDER_DELAY + 1] :

ByteIOContext

Eine Struktur, in der Informationen zu einer Filmdatei auf niedriger Ebene gespeichert werden.

vorzeichenloser char * buffer :
int buffer_size :
vorzeichenloser char * buf_ptr :
vorzeichenloser char * buf_end :
void * opaque :
int (* read_packet) (void * opaque, uint8_t * buf, int buf_size) :
int (* write_packet) (void * opaque, uint8_t * buf, int buf_size) :
offset_t (* seek) (void * undurchsichtig, offset_t offset, int woher) :
offset_t pos :
int must_flush :
int eof_reached :
int write_flag :
int is_streamed :
int max_packet_size :
vorzeichenlose lange Prüfsumme :
vorzeichenlose char * Prüfsumme_ptr :
vorzeichenlose lange (* update_checksum) (vorzeichenlose lange Prüfsumme:
const uint8_t * buf, vorzeichenlose int-Größe)
:
int error : enthält den Fehlercode oder 0, wenn kein Fehler aufgetreten ist.

SDL_AudioSpec

Wird verwendet, um das Format einiger Audiodaten zu beschreiben.

Frequenz : Schallfrequenz in Samples pro Sekunde.
Format : Audiodatenformat.
Kanäle : Anzahl der Kanäle: 1 - Mono, 2 - Stereo, 4 Surround, 6 Surround mit Zentrierung und LFE-
Stille : Stillewert des Schallpuffers (berechnet).
Samples : Audiopuffergröße in Samples.
Größe : Die Größe des Audiopuffers in Bytes (berechnet).
Rückruf (..) : Rückruffunktion zum Füllen des Audiopuffers.
Benutzerdaten : Ein Zeiger auf Benutzerdaten, der an die Rückruffunktion übergeben wird.

Die folgenden Formatwerte sind

gültig : AUDIO_U8 - ​​8-Bit-Samples ohne Vorzeichen.
AUDIO_S8 - signierte 8-Bit-Samples.
AUDIO_U16 oder AUDIO_U16LSB - wird nicht von der gesamten Hardware unterstützt (vorzeichenlose 16-Bit-Low-Byte-Reihenfolge).
AUDIO_S16 oder AUDIO_S16LS - nicht von der gesamten Hardware unterstützt (16-Bit mit alter Bytereihenfolge)
AUDIO_U16MSB - nicht von der gesamten Hardware unterstützt (vorzeichenloser 16-Bit-Big-Endian).
AUDIO_S16MS - wird nicht von der gesamten Hardware unterstützt (16-Bit mit hoher Bytereihenfolge).
AUDIO_U16SYS: entweder AUDIO_U16LSB oder AUDIO_U16MSB - je nach Hardwareprozessor.
AUDIO_S16SYS: entweder AUDIO_S16LSB oder AUDIO_S16MSB - je nach Hardwareprozessor.

SDL_Event

Die Grundstruktur für Ereignisse.

Typ : Art des Ereignisses.
aktiv : Aktivierungsereignis (siehe SDL_ActiveEvent).
key : Tastaturereignis (siehe SDL_KeyboardEvent).
Bewegung : Mausbewegungsereignis (siehe SDL_MouseMotionEvent).
Schaltfläche : Mausklickereignis (siehe SDL_MouseButtonEvent).
jaxis : Bewegungsereignis der Joystickachse (siehe SDL_JoyAxisEvent).
jball : Joystick-Trackball-Bewegungsereignis (siehe SDL_JoyBallEvent).
jhat : Joystick-Header-Bewegungsereignis (siehe SDL_JoyHatEvent).
jbutton : Ereignis beim Drücken der Joystick-Taste (siehe SDL_JoyButtonEvent).
Größe ändern: Ereignis zur Größenänderung des Anwendungsfensters (siehe SDL_ResizeEvent).
expose : Ereignis zum Öffnen des Anwendungsfensters (siehe SDL_ExposeEvent).
Beenden : Anwendungs-Exit-Anforderungsereignis (siehe SDL_QuitEvent).
Benutzer : Benutzerereignis (siehe SDL_UserEvent).
syswm : undefiniertes Fenstermanagerereignis (siehe SDL_SysWMEvent).

Hier sind die Arten von Ereignissen. Siehe SDL - Dokumentation Weitere Informationen :.

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

SDL_Overlay

YUV-Overlay.

Format : Überlagerungsformat (siehe unten).
w, h : Breite / Höhe der Überlagerung.
Flugzeuge : Die Anzahl der zu überlagernden Pläne. Normalerweise entweder 1 oder 3.
Tonhöhen : eine Reihe von Einrückungen, eine für jeden Plan. Einzug ist die Länge der Zeichenfolge in Bytes.
Pixel : Ein Array von Datenzeigern für jeden Plan. Die Überlagerung muss gesperrt sein, bevor diese Zeiger verwendet werden können.
hw_overlay : Wird auf 1 gesetzt, wenn das Overlay hardwarebeschleunigt ist.

SDL_Rect

Der rechteckige Bereich.

Sint16 x, y : Position der oberen linken Ecke des Rechtecks.
Uint16 w, h : Breite und Höhe des Rechtecks.

SDL_Rect definiert einen rechteckigen Pixelbereich. Es wird von SDL_BlitSurface verwendet, um Blitting-Bereiche und einige andere Videofunktionen zu identifizieren.

SDL_Surface

Grafische Struktur der Außenseite (Oberfläche).

Uint32-Flags : Flags der externen Stotrons. Nur zum Lesen.
SDL_PixelFormat * -Format : schreibgeschützt.
int w, h : Breite und Höhe. Nur zum Lesen.
Uint16 Tonhöhe : Schritt. Nur zum Lesen.
void * pixels : Ein Zeiger auf die tatsächlichen Pixeldaten. Nur zur Aufnahme.
SDL_Rect clip_rect : Die rechteckige Außenseite des Clips. Nur zum Lesen.
int refcount : Wird zum Zuweisen von Speicher verwendet. Meistens zum Lesen.
Diese Struktur enthält auch private Felder, die hier nicht angezeigt werden.

SDL_Surface repräsentiert einen Bereich des "grafischen" Speichers, der gezeichnet werden kann. Der Videopufferrahmen wird mit SDL_SetVideoMode und SDL_GetVideoSurface als SDL_Surface zurückgegeben. Die Felder w und h sind Werte, die die Breite und Höhe der Oberfläche in Pixel darstellen. Das Pixelfeld ist ein Zeiger auf die tatsächlichen Pixeldaten. Hinweis: Die Oberfläche muss gesperrt sein (über SDL_LockSurface), bevor auf dieses Feld zugegriffen werden kann. Das Feld clip_rect ist das von SDL_SetClipRect festgelegte abgeschnittene Rechteck.

Das Flag-Feld unterstützt die folgenden ODER-Werte:

SDL_SWSURFACE - Die Außenseite wird im Systemspeicher gespeichert.
SDL_HWSURFACE - Die Außenseite wird im Videospeicher gespeichert.
SDL_ASYNCBLIT - Die Außenseite verwendet nach Möglichkeit asynchrone Blendung.
SDL_ANYFORMAT - Jedes Pixelformat (Anzeigefläche) ist zulässig.
SDL_HWPALETTE - Die Oberfläche verfügt über eine exklusive Palette.
SDL_DOUBLEBUF - doppelt gepufferte Oberfläche (Anzeigefläche).
SDL_FULLSCREEN - Vollbildfläche (Anzeigefläche).
SDL_OPENGL - Die Oberfläche hat einen OpenGL-Kontext (Anzeigefläche).
SDL_OPENGLBLIT - Die Oberfläche unterstützt das Blining von OpenGL (Anzeigefläche). HINWEIS. Diese Option dient nur der Kompatibilität und wird für neuen Code nicht empfohlen.
SDL_RESIZABLE - Die Größenänderung ist für eine Oberfläche (Anzeigefläche) möglich.
SDL_HWACCEL - Surface Blit verwendet Hardwarebeschleunigung.
SDL_SRCCOLORKEY - Oberflächlichkeit verwendet Farbblitting.
SDL_RLEACCEL - Farbblitting wird mit RLE beschleunigt.
SDL_SRCALPHA - Surface Blyth verwendet Alpha-Blending.
SDL_PREALLOC - Die Oberfläche verwendet vorab zugewiesenen Speicher.

SDL_Thread

Diese Struktur ist systemunabhängig und muss wahrscheinlich nicht verwendet werden. Weitere Informationen finden Sie unter src / thread / sdl_thread_c.h im Quellcode.

SDL_cond

Diese Struktur ist systemunabhängig und muss wahrscheinlich nicht verwendet werden. Weitere Informationen finden Sie im Quellcode unter src / thread / <system> /SDL_syscond.c.

SDL_mutex

Diese Struktur ist systemunabhängig und muss wahrscheinlich nicht verwendet werden. Weitere Informationen finden Sie im Quellcode unter src / thread / <system> /SDL_sysmutex.c.






Links


Ein FFmpeg- und SDL-Tutorial oder Schreiben eines Videoplayers in weniger als 1000 Zeilen

FFmpeg , SDL FFmpeg HomePage SDL HomePage











Lesen Sie auch im Blog der Firma EDISON:


FFmpeg libav Handbuch

All Articles