[5] FFMPEG+SDL2 implementation of the audio player V2.0 (no noise)

Source: Internet
Author: User
Tags assert comments mutex valid

Date: 2016.10.4
Author: isshe
Github:github.com/isshe
E-mail: i.sshe@outlook.com
Platform: ubuntu16.04 64bit 1. Preface

So far, we have learned and recorded the contents of the FFMPEG+SDL2 display video and events (event).
This record FFMPEG+SDL2 play audio, did not join the event processing.
Next add the event processing and continue to learn the audio and video synchronization, and then added a pause or the like or add an interface. 2. Flowchart

3. Example


The main idea of the sample code is: (and the audio player V1.0 the idea, the implementation is different.) The difference is that the program uses a queue to store the main thread to read the Avpacket) The main thread is only responsible for reading Avpacket to the queue. ->av_read_frame () All other decoding, output work is done by callback.
Callback from the queue to take avpacketlist, and then avpacketlist in the Avpacket out to decode. After decoding, place the buffer and then output. One call to callback, the output length Len data (callback the third parameter, generally 2048), not much more. When the buffer has no data, it is decoded, put into the buffer, and then output. When more data is decoded than Len, only Len is processed, and the rest is left to the subsequent callback. The callback function is called again and again until all processing is done.

See the code and comments in the code

Note: The list structure is avpacketlist, with two members:
Avpacket packet; Avpacketlist *next; Code commented out in code (block comments), is not and can run normally, to be more robust and so on. 3.1 Code

(All the code is pasted, longer, followed by a code download address) audio_player_v2.0.c

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include < Unistd.h> #define __stdc_constant_macros//ffmpeg requires #ifdef __cplusplus extern "C" {#endif #include <libavco dec/avcodec.h> #include <libavformat/avformat.h> #include <libswresample/swresample.h> #include < Sdl2/sdl.h> #ifdef __cplusplus} #endif//#include "wrap_base.h" #include "packet_queue.h" #define Avcode_max_audio               _frame_size 192000//1 Second of 48khz 32bit audio #define SDL_AUDIO_BUFFER_SIZE 1024x768//#define File_name "/HOME/ISSHE/MUSIC/WAVINFLAG.AAC" #define Err_stream stderr #define Out_sample_rate 44100 A
Vframe Wanted_frame;
Packetqueue Audio_queue;

int quit = 0;
void Audio_callback (void *userdata, Uint8 *stream, int len);
int Audio_decode_frame (Avcodeccontext *pcodec_ctx, uint8_t *audio_buf, int buf_size);
/* void Packet_queue_init (Packetqueue *queue); int PackeT_queue_put (Packetqueue *queue, Avpacket *packet);
int Packet_queue_get (Packetqueue *queue, Avpacket *packet, int block);
    */int main (int argc, char *argv[]) {Avformatcontext *pformat_ctx = NULL;
    int audio_stream =-1;
    Avcodeccontext *pcodec_ctx = NULL;
    Avcodeccontext *PCODEC_CTX_CP = NULL;
    Avcodec *pcodec = NULL;        Avpacket packet;
    //!
    Avframe *pframe = NULL;

    Char filename[256] = file_name;
    SDL Sdl_audiospec Wanted_spec;

    Sdl_audiospec spec;

    FFmpeg initialization of Av_register_all (); SDL Initialize if (Sdl_init (Sdl_init_video | Sdl_init_audio |
        Sdl_init_timer) {fprintf (Err_stream, "couldn ' t INIT sdl:%s\n", Sdl_geterror ());
    Exit (-1);

    } get_file_name (filename, argc, argv); Open File if (avformat_open_input (&pformat_ctx, filename, NULL, NULL)! = 0) {fprintf (Err_stream, "Could N ' t open input File\n ");
    Exit (-1);
         }//Detect file stream information//Legacy version is Av_find_stream_info () if (Avformat_find_stream_info (Pformat_ctx, NULL) < 0) {
         fprintf (Err_stream, "not Found STREAM info\n");
    Exit (-1);

    }//Display file information, very useful for a function av_dump_format (pformat_ctx, 0, filename, false);  Find the AV stream if (find_stream_index (Pformat_ctx, NULL, &audio_stream) = =-1) {fprintf (Err_stream, "couldn ' t
        Find Stream index\n ");
    Exit (-1);

    } printf ("Audio_stream =%d\n", audio_stream);
    Find the corresponding decoder pcodec_ctx = pformat_ctx->streams[audio_stream]->codec;
    Pcodec = Avcodec_find_decoder (pcodec_ctx->codec_id);
         if (!pcodec) {fprintf (Err_stream, "couldn ' t find decoder\n");
    Exit (-1);
    }/* PCODEC_CTX_CP = AVCODEC_ALLOC_CONTEXT3 (PCODEC); if (Avcodec_copy_context (PCODEC_CTX_CP, pcodec_ctx)! = 0) {fprintf (Err_stream, "couldn" t copy codec context\n
        ");
    Exit (-1); }
 */

    //Set Audio information to open the audio device.
    Wanted_spec.freq = pcodec_ctx->sample_rate;
    Wanted_spec.format = Audio_s16sys;        Wanted_spec.channels = pcodec_ctx->channels;    Number of channels wanted_spec.silence = 0;        Set Mute value wanted_spec.samples = sdl_audio_buffer_size;
    Adjust after reading the first frame?
    Wanted_spec.callback = Audio_callback;

    Wanted_spec.userdata = Pcodec_ctx;
    Wanted_spec: Want to open the//spec: actually open, can not use this, function directly with NULL, the following use of the spec Wanted_spec instead.
    This will open a thread that calls callback.
    Sdl_openaudio->open_audio_device (thread)->sdl_runaudio->fill (point to callback function)//can be replaced with Sdl_openaudiodevice () if (Sdl_openaudio (&wanted_spec, &spec) < 0) {fprintf (Err_stream, "couldn ' t open audio:%s\n", sdl_g
        Eterror ());
    Exit (-1);
    }//Set parameters for decoding time, swr_alloc_set_opts in part parameter Wanted_frame.format = AV_SAMPLE_FMT_S16;
    Wanted_frame.sample_rate = Spec.freq; Wanted_frame.channel_layout = av_get_default_channel_layout (spec. Channels);

    Wanted_frame.channels = Spec.channels;
    Initialize the Avcondeccontext, and do some processing work.

    Avcodec_open2 (Pcodec_ctx, Pcodec, NULL);

    Initialize Queue packet_queue_init (&audio_queue);
    Can be replaced with Sdl_pauseaudiodevice (), the current use of this should be old.              0 is not suspended, not 0 is suspended//if there is no sound sdl_pauseaudio (0);

    Why do you want this? Read a frame of data while (Av_read_frame (Pformat_ctx, &packet) >= 0)//read a packet, data placed in Packet.data {if (pack
         Et.stream_index = = Audio_stream) {packet_queue_put (&audio_queue, &packet);
         } else {av_free_packet (&packet);      }} getchar ();
... return 0;
}//Note that UserData is the front avcodeccontext.
The value of Len is usually 2048, which indicates how much is sent at a time.
Audio_buf_size: Always the size of the sample buffer, Wanted_spec.samples. (generally decoding so much each time, the file is different, this value is different)//audio_buf_index: where the tag was sent. The working mode of this function is://1. The decoded data is put into audio_buf, and the size is audio_buf_size.
(audio_buf_size = audio_size; This sentence is set)//2. A callback can only send Len bytes at a time, and the decoded data for each fetch may be larger than Len and will not be completed at one time.3.
Can not finish the time, will len = = 0, do not continue to loop, exit the function, continue to call callback, for the next send. 4.
Because the last time did not finish, this time do not take data, send the last remaining, Audio_buf_size Mark sent to where. 5.
Note that callback must send only Len data at a time, otherwise it will not exit. If it is not enough, the buffer is gone, and then it is taken.
If you have enough, quit and leave it to the next hair cycle.
Three variables are set to static to save the last data, or to use global variables, but it feels better. void Audio_callback (void *userdata, Uint8 *stream, int len) {Avcodeccontext *pcodec_ctx = (Avcodeccontext *) User
     Data
     int len1 = 0;

     int audio_size = 0;
     Note is static//why to divide so big.
     Static uint8_t audio_buf[(Avcode_max_audio_frame_size * 3)/2];
     static unsigned int audio_buf_size = 0;

     static unsigned int audio_buf_index = 0;
     Initialize the stream, every time.

     Sdl_memset (stream, 0, Len);
              while (Len > 0) {if (Audio_buf_index >= audio_buf_size) {//data are all sent, then get
              A custom Function audio_size = Audio_decode_frame (Pcodec_ctx, Audio_buf, sizeof (AUDIO_BUF));
            if (Audio_size < 0) {//Error Mute      Audio_buf_size = 1024;
              memset (audio_buf, 0, audio_buf_size);
              } else {audio_buf_size = audio_size;      } audio_buf_index = 0;
Back to the beginning of the buffer} len1 = Audio_buf_size-audio_buf_index;
          printf ("len1 =%d\n", len1);
          if (Len1 > Len)//len1 is often larger than Len, but a callback can only be sent len {len1 = len; }//The new program replaces//mixed audio with Sdl_mixaudioformat (), the first parameter is DST, the second is src,audio_buf_size change every time Sdl_mixaudio
          (Stream, (uint8_t*) Audio_buf + Audio_buf_index, Len, Sdl_mix_maxvolume);
          memcpy (Stream, (uint8_t *) Audio_buf + audio_buf_index, len1);
          Len-= len1;
          Stream + = Len1;
     Audio_buf_index + = Len1;

}}//For audio, a packet inside, may contain multi-frame (frame) data. int Audio_decode_frame (Avcodeccontext *pcodec_ctx, uint8_t *audio_buf, int buf_size) {Avpacket PACket;
     Avframe *frame;
     int got_frame;
int pkt_size = 0;
     uint8_t *pkt_data = NULL;
     int Decode_len;
     int try_again = 0;
     Long long audio_buf_index = 0;
     Long long data_size = 0;
     Swrcontext *swr_ctx = NULL;
     int convert_len = 0;

     int convert_all = 0; if (Packet_queue_get (&audio_queue, &packet, 1) < 0) {fprintf (Err_stream, "Get queue packet Erro
          r\n ");
     return-1;
     }//pkt_data = Packet.data;
Pkt_size = packet.size;

     fprintf (Err_stream, "pkt_size =%d\n", pkt_size);
     frame = Av_frame_alloc ();
          while (Pkt_size > 0) {//memset (frame, 0, sizeof (avframe)); PCODEC_CTX: Decoder information//frame: output, save data to frame//got_frame: output.
          0 represents a frame taken, which does not mean that an error has occurred.
          Packet: input, FETCH data decoding.
        Decode_len = Avcodec_decode_audio4 (Pcodec_ctx, Frame, &got_frame, &packet);  if (Decode_len < 0)//decoding error {//re-solution, here if always <0 it.
               fprintf (Err_stream, "couldn ' t decode frame\n");
                    if (Try_again = = 0) {try_again++;
               Continue
          } try_again = 0; } if (Got_frame) {/*//To get sample buffer size with fixed audio parameters data_size = Av_samples_get_bu Ffer_size (NULL, Pcodec_ctx->channels, Frame->nb_samples, PCODEC_CTX-&G

               T;SAMPLE_FMT, 1);
ASSERT (Data_size <= buf_size);
memcpy (Audio_buf + Audio_buf_index, frame->data[0], data_size);
              *///chnanels: Number of channels, only for audio//channel_layout: Channel layout.
              Multi-Audio channel flow, a channel layout can describe its configuration. The concept of channel layout is not understood.
              It's about mono (mono), stereo channel (stereo), four-channel. See source code and: https://xdsnet.gitbooks.io/other-doc-cn-ffmpeg/content/ffmpeg-doc-cn-07.html#%E9%80%9a%e9%81%93%e5%b8%83%e5%b1%80 if (frame->channels > 0 && frame->channel_layout = = 0)
                   {//Get the default layout, the default should be stereo.
              Frame->channel_layout = Av_get_default_channel_layout (frame->channels);
                  } else if (Frame->channels = = 0 && frame->channel_layout > 0) {
              Frame->channels = Av_get_channel_layout_nb_channels (frame->channel_layout);
                   } if (Swr_ctx! = NULL) {swr_free (&AMP;SWR_CTX);
              Swr_ctx = NULL;
              }//Set Common parameters//2,3,4 is the output parameter, 4,5,6 is the input parameter. 
                      Swr_ctx = swr_alloc_set_opts (NULL, Wanted_frame.channel_layout, (Avsampleformat) Wanted_frame.format, Wanted_frame.sample_rate, Frame->channel_layout, (Avsampleformat) frame->format, frame->sample_rate, 0, NULL); Initialize if (Swr_ctx = = NULL | | swr_init (SWR_CTX) < 0) {fprintf (err_stream
                   , "Swr_init error\n");
              Break
              }//av_rescale_rnd (): Rounds the team 64bit integers in the specified way (rnd:rounding),//so that operations such as a*b/c do not overflow.
    Swr_get_delay (): Returns one of the second parameter points (below is: 1/frame->sample_rate)//avrouding is a enum,1 meaning round away from zero. /* int dst_nb_samples = AV_RESCALE_RND (Swr_get_delay (Swr_ctx, Frame->sample_rat e) + frame->nb_samples, wanted_frame.sample_rate, Wanted_frame.format, AVRo
    Unding (1)); *///convert audio.
              Convert the audio in frame to audio_buf.
              2nd, the 3 parameter is output, 4th, 5 is input.
              You can use # define Avcode_max_audio_frame_size 192000//To replace the dst_nb_samples, the maximum sampling frequency is 192kHz. Convert_len = Swr_convert (swr_ctX, &audio_buf + Audio_buf_index, Avcode_max_audio_fra Me_size, (const uint8_t * *) Frame->data, frame->nb_s

              Amples);
              printf ("Decode len =%d, Convert_len =%d\n", Decode_len, Convert_len);
              Decoded how much, decoded to where//pkt_data + = Decode_len;
              Pkt_size-= Decode_len;
              Where is the converted valid data stored, and the audio_buf_index tag audio_buf_index + = Convert_len;//data_size;
         Returns the length of all converted valid data convert_all + = Convert_len;
}} return Wanted_frame.channels * Convert_all * av_get_bytes_per_sample ((Avsampleformat) wanted_frame.format);
return audio_buf_index; }
packet_queue.h
#ifndef packet_queue_h_
#define PACKET_QUEUE_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/ swresample.h>
#include <SDL2/SDL.h>

typedef struct PACKETQUEUE
{
    avpacketlist    * FIRST_PKT;     A packet of the head of the team, note that the type is not avpacket
    avpacketlist    *last_pkt;      Team Tail Packet
    int             nb_packets;     Paket number
    int             size;
    Sdl_mutex       *mutex;
    sdl_cond        *cond;          Condition variable
}packetqueue;

void Packet_queue_init (Packetqueue *queue);
int Packet_queue_put (Packetqueue *queue, Avpacket *packet);
int Packet_queue_get (Packetqueue *queue, Avpacket *pakcet, int block);

#ifdef __cplusplus
}
#endif
#endif
packet_queue.c
#define __stdc_constant_macros//ffmpeg requires #ifdef __cplusplus extern "C" {#endif #include <libavcodec/avcodec.h

> #include <libavformat/avformat.h> #include <libswresample/swresample.h> #include <SDL2/SDL.h> #ifdef __cplusplus} #endif #include "packet_queue.h"/*============================================================ ==========\ * Author (author): I.sshe * Date (date): 2016/10/03 * Others (Other): Initialize queue \*=========================== ============================================*/void Packet_queue_init (Packetqueue *queue) {//memset (queue, 0, sizeof
     (Packetqueue));
     QUEUE-&GT;FIRST_PKT = NULL;
     QUEUE-&GT;LAST_PKT = NULL;
     Queue->mutex = Sdl_createmutex ();
Queue->cond = Sdl_createcond (); }/*======================================================================\ * Author (author): I.sshe * Date (date) : 2016/10/03 * Others (Other): Queue \*=======================================================================*/int Packet_queue_put (packetqueue *queue, Avpacket *packet) {avpacketlist *pkt_list;
     // ???
     if (Av_dup_packet (packet) < 0) {return-1;
     } pkt_list = (Avpacketlist *) av_malloc (sizeof (avpacketlist));
     if (pkt_list = = NULL) {return-1;
     } pkt_list->pkt = *packet;

     Pkt_list->next = NULL;

     Lock Sdl_lockmutex (Queue->mutex);
     if (QUEUE-&GT;LAST_PKT = = null)//empty team {queue->first_pkt = pkt_list;
     } else {queue->last_pkt->next = pkt_list;  } queue->last_pkt = Pkt_list;
     Here queue->last_pkt = Queue->last_pkt->next meaning, however, more cases are handled.
     queue->nb_packets++;
     Queue->size + = packet->size;      Sdl_condsignal (Queue->cond);

     //???

     Sdl_unlockmutex (Queue->mutex);
return 0; }/*======================================================================\ * Author (author): I. Sshe * Date (date): 2016/10/03 * Others (Other): Team \*==============================================================
=========*/extern int quit;
     int Packet_queue_get (Packetqueue *queue, Avpacket *pkt, int block) {avpacketlist *pkt_list = NULL;

     int ret = 0;

     Sdl_lockmutex (Queue->mutex);
              while (1) {if (quit) {ret =-1;
          Break
          } pkt_list = queue->first_pkt;    if (pkt_list! = NULL)//team is not empty, there is data {queue->first_pkt = queue->first_pkt->next; Pkt_list->next if (queue->first_pkt = = NULL) {QUEUE-&GT;LAST_PKT
              = NULL;
              } queue->nb_packets--; Queue->size-= Pkt_list->pkt

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.