ffmpeg multi-threaded simple player-carding of main functions

Posted by reece_1989 on Thu, 10 Mar 2022 19:27:05 +0100

Mainly the next few functions

First is the basic definition of the main function

VideoState      *is;
is = av_mallocz(sizeof(VideoState));
 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
    fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
    exit(1);
  }
  is->pictq_mutex = SDL_CreateMutex();
  is->pictq_cond = SDL_CreateCond();
  //set timer
    //Set callback timer rendering! Render every 40ms Here's the rendering
  schedule_refresh(is, 40);

Then start the unwrapping thread

 is->parse_tid = SDL_CreateThread(decode_thread, "decode_thread", is);

Open Unpacking decode_thread()

Get file information
 if(avformat_open_input(&pFormatCtx, is->filename, NULL, NULL)!=0)
 Getting Stream index 
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) {
   //Initialize Various
    stream_component_open(is, audio_index);
  }
  if(video_index >= 0) {
    stream_component_open(is, video_index);
  }   
  SDL Initialization
win = SDL_CreateWindow("Media Player",
     		   SDL_WINDOWPOS_UNDEFINED,
		   SDL_WINDOWPOS_UNDEFINED,
		   is->video_ctx->width, is->video_ctx->height,
		   SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
  
  renderer = SDL_CreateRenderer(win, -1, 0);

  pixformat = SDL_PIXELFORMAT_IYUV;
  texture = SDL_CreateTexture(renderer,
			      pixformat, 
			      SDL_TEXTUREACCESS_STREAMING,
			      is->video_ctx >width,
			      is->video_ctx->height);

  // Cyclic Unpacking
  for(;;) {

    if(is->quit) {
      SDL_CondSignal(is->videoq.cond);
      SDL_CondSignal(is->audioq.cond);
      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);
      //fprintf(stderr, "put video queue, size :%d\n", is->videoq.nb_packets);
    } else if(packet->stream_index == is->audioStream) {
      packet_queue_put(&is->audioq, packet);
      //fprintf(stderr, "put audio queue, size :%d\n", is->audioq.nb_packets);
    } else {
      av_free_packet(packet);
    }

  }
	
    // Determine what stream queues the demultiplexed pkt
    if(packet->stream_index == is->videoStream) {
      packet_queue_put(&is->videoq, packet);
      //fprintf(stderr, "put video queue, size :%d\n", is->videoq.nb_packets);
    } else if(packet->stream_index == is->audioStream) {
      packet_queue_put(&is->audioq, packet);
      //fprintf(stderr, "put audio queue, size :%d\n", is->audioq.nb_packets);
    } else {
      av_free_packet(packet);
    }

  }
 

Initialization and decoding-related function stream_component_open(is, video_index);

Initializing parameters and creating video decoding threads

General Initialization
  int64_t in_channel_layout, out_channel_layout;

  AVFormatContext *pFormatCtx = is->pFormatCtx;
  AVCodecContext *codecCtx = NULL;
  AVCodec *codec = NULL;
  SDL_AudioSpec wanted_spec, spec;
  Initialize decoder based on stream information
 codec = avcodec_find_decoder(pFormatCtx->streams[stream_index]->codec->codec_id);

  codecCtx = avcodec_alloc_context3(codec);
  Determine flow type, flow type, operation
 case AVMEDIA_TYPE_AUDIO:
 case AVMEDIA_TYPE_VIDEO:

case AVMEDIA_TYPE_AUDIO:

Various Initializations

//First pass in the audio parameter to ensure that the device can turn on 2 sets the callback function decode the callback function audio_callback and callback parameters
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 = 2;
    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;
    }
  }; 
  Initialization audiostream Related parameters 
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;
    open up pktk space
    memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
    //Initialize Queue
    packet_queue_init(&is->audioq);
    //Turn on playing audio
    SDL_PauseAudio(0);
//Out Audio Param

Set Resampling Related Parameters to Initialize Resampling
    uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;

    //AAC:1024  MP3:1152
    int out_nb_samples= is->audio_ctx->frame_size;
    //AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;

    int out_sample_rate=is->audio_ctx->sample_rate;
    int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);
    //Out Buffer Size
    /*
    int out_buffer_size=av_samples_get_buffer_size(NULL,
                                                   out_channels,
                                                   out_nb_samples,
                                                   AV_SAMPLE_FMT_S16,
                                                   1);
                                                   */

    //uint8_t *out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
    int64_t in_channel_layout=av_get_default_channel_layout(is->audio_ctx->channels);

    struct SwrContext *audio_convert_ctx = NULL;
    audio_convert_ctx = swr_alloc();
    if(!audio_convert_ctx){
        printf("Failed to swr_alloc\n"); 
        return -1;
    }
    swr_alloc_set_opts(audio_convert_ctx,
                       out_channel_layout,
                       AV_SAMPLE_FMT_S16,
                       out_sample_rate,
                       in_channel_layout,
                       is->audio_ctx->sample_fmt,
                       is->audio_ctx->sample_rate,
                       0,
                       NULL);
swr_init(audio_convert_ctx);
is->audio_swr_ctx = audio_convert_ctx; 
    

case AVMEDIA_TYPE_VIDEO

The focus here is on the initialized video decoding thread

is->videoStream = stream_index;
    is->video_st = pFormatCtx->streams[stream_index];
    is->video_ctx = codecCtx;
    //Initialize Queue
    packet_queue_init(&is->videoq);
    
    is->video_tid = SDL_CreateThread(video_thread, "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, 
				 AV_PIX_FMT_YUV420P,
				 SWS_BILINEAR, 
				 NULL, NULL, NULL);
   

Video decoding thread video_thread

Is to loop pkt out of the queue and decode it to frame and place it in the decoded video queue

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

  pFrame = av_frame_alloc();

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

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

    // Did we get a video frame?
    if(frameFinished) {
      if(queue_picture(is, pFrame) < 0) {
	break;
      }      
    }

    av_free_packet(packet);
  }
  av_frame_free(&pFrame);
  return 0;
}

Audio decoding void audio_in main thread Callback (void *userdata, Uint8 *stream, int len)

First get parameter 1 from the parameter we defined passed in. Buffer 3 buffer size for global context 2 sound card

First judge audio_ If buf has any value in it then go to the queue to get pkt decoding
Then calculate that if the buf length and the buffer length are smaller, all inputs will be larger than the input buffer size only

Then redefine buf based on the amount of incoming data

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

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

  SDL_memset(stream, 0, len);

  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));
      if(audio_size < 0) {
	/* If error, output silence */
	is->audio_buf_size = 1024*2*2;
	memset(is->audio_buf, 0, is->audio_buf_size);
      } else {
	is->audio_buf_size = audio_size;
      }
      is->audio_buf_index = 0;
    }
    len1 = is->audio_buf_size - is->audio_buf_index;
    fprintf(stderr, "stream addr:%p, audio_buf_index:%d, audio_buf_size:%d, len1:%d, len:%d\n",
		    stream,
	  	    is->audio_buf_index, 
                    is->audio_buf_size, 
		    len1, 
		    len);
                    
    if(len1 > len)
      len1 = len;
    //memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
    //fwrite(is->audio_buf, 1, len1, audiofd1);
    //fflush(audiofd1);
    SDL_MixAudio(stream,(uint8_t *)is->audio_buf + is->audio_buf_index, len1, SDL_MIX_MAXVOLUME);
    len -= len1;
    stream += len1;
    is->audio_buf_index += len1;
  }
}

Audio decoding int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size)

Return value is decoded data
Parameter 1 is the global context
Parameter 2 is the buf to fill in the frame
bufsize of parameter 3
Loop decoding
First determine if there is a pkt in the queue that gets decoded every day, then call the previously defined resampling format to resample it, write it to the buffer, and then redefine buf size and buf

After video decoding

Manual callback defines how many seconds to call back a function schedule_refresh()
A callback function sdl_ Refresh_ Timer_ SDL_where cb() sends an event main function Event Receives Events
SDL_ Evet calls video_refresh_timer();
video_refresh_timer(); Is the logic of true video rendering
Inside it, check to see if there is data after the decoded queue, no data for a millisecond, and then again until there is data
With data that sets a schedule_ Refresh (40ms) call again after 40ms and so on
Then refresh the SDL display

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

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

    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 */
                fprintf(stderr, "Failed to decode audio ......\n");
                is->audio_pkt_size = 0;
                break;
            }

            data_size = 0;
            if(got_frame) {

                /*
            fprintf(stderr, "=====>auido: channels:%d, nb_samples:%d, sample_fmt:%d\n",
                    is->audio_ctx->channels,
                    is->audio_frame.nb_samples,
                    is->audio_ctx->sample_fmt);
                                */

                /*
            data_size = av_samples_get_buffer_size(NULL,
                                   is->audio_ctx->channels,
                                   is->audio_frame.nb_samples,
                                   is->audio_ctx->sample_fmt,
                                   1);
            */
                data_size = 2 * is->audio_frame.nb_samples * 2;
                assert(data_size <= buf_size);
                //memcpy(audio_buf, is->audio_frame.data[0], data_size);

                swr_convert(is->audio_swr_ctx,
                            &audio_buf,
                            MAX_AUDIO_FRAME_SIZE*3/2,
                            (const uint8_t **)is->audio_frame.data,
                            is->audio_frame.nb_samples);


                fwrite(audio_buf, 1, data_size, audiofd);
                fflush(audiofd);
            }

            is->audio_pkt_data += len1;
            is->audio_pkt_size -= len1;
            if(data_size <= 0) {
                /* No data yet, get more frames */
                continue;
            }
            /* We have data, return it and come back for more later */
            return data_size;
        }

        if(pkt->data)
            av_free_packet(pkt);

        if(is->quit) {
            fprintf(stderr, "will quit program......\n");
            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;
    }
}

Topics: ffmpeg