Exemple #1
0
//1 get one frame 0 failed -1 err
int ffmpeg_adec_decode(ad_wrapper_t *wrapper, adec_ctrl_t *pinfo)
{
    int got_samples = 0;
    int ret = 0;
    int data_size = 0;
    AVFrame frame_tmp;
    //AVFrame frame;
    AVPacket pkt;
    memset(&pkt, 0, sizeof(AVPacket));
    pkt.data = pinfo->inptr;
    pkt.size = pinfo->inlen;
    pkt.side_data_elems = 0;
    memset(&frame_tmp, 0, sizeof(AVFrame));
    memset(frame, 0, sizeof(AVFrame));

    dtaudio_decoder_t *decoder = (dtaudio_decoder_t *)wrapper->parent;
    dt_debug(TAG, "start decode size:%d %02x %02x \n", pkt.size, pkt.data[0],
             pkt.data[1]);
    ret = avcodec_decode_audio4(avctxp, frame, &got_samples, &pkt);
    dt_debug(TAG, "start decode size:%d %02x %02x %02x %02x \n", pkt.size,
             pkt.data[0], pkt.data[1], pkt.data[2], pkt.data[3]);
    if (ret < 0) {
        dt_error(TAG, "decode failed ret:%d \n", ret);
        goto EXIT;
    }

    if (!got_samples) {         //decode return 0
        dt_error(TAG, "get no samples out \n");
        pinfo->outlen = 0;
        goto EXIT;
    }
    data_size = av_samples_get_buffer_size(frame->linesize, avctxp->channels,
                                           frame->nb_samples, avctxp->sample_fmt, 1);
    if (data_size > 0) {
        audio_convert(decoder, &frame_tmp, frame);
        //out frame too large, realloc out buf
        if (pinfo->outsize < frame_tmp.linesize[0]) {
            pinfo->outptr = realloc(pinfo->outptr, frame_tmp.linesize[0] * 2);
            pinfo->outsize = frame_tmp.linesize[0] * 2;
        }

        memcpy(pinfo->outptr, frame_tmp.data[0], frame_tmp.linesize[0]);
        pinfo->outlen = frame_tmp.linesize[0];
    } else {
        dt_error(TAG, "data_size invalid: size:%d outlen:%d \n", data_size,
                 pinfo->outlen);
        pinfo->outlen = 0;
    }

EXIT:
    if (frame_tmp.data[0]) {
        free(frame_tmp.data[0]);
    }
    frame_tmp.data[0] = NULL;
    return ret;
}
Exemple #2
0
static int demuxer_ffmpeg_read_frame (demuxer_wrapper_t * wrapper, dt_av_frame_t * frame)
{
    dtdemuxer_context_t *dem_ctx = (dtdemuxer_context_t *) wrapper->parent;
    dt_media_info_t *media_info = &dem_ctx->media_info;

    int has_audio = (media_info->no_audio) ? 0 : media_info->has_audio;
    int has_video = (media_info->no_video) ? 0 : media_info->has_video;
    int has_sub = (media_info->no_sub) ? 0 : media_info->has_sub;

    int cur_aidx = (has_audio) ? media_info->astreams[media_info->cur_ast_index]->index : -1;
    int cur_vidx = (has_video) ? media_info->vstreams[media_info->cur_vst_index]->index : -1;
    int cur_sidx = (has_sub) ? media_info->sstreams[media_info->cur_sst_index]->index : -1;

    AVFormatContext *ic = (AVFormatContext *) wrapper->demuxer_priv;
    AVPacket avpkt;
    int ret = av_read_frame (ic, &avpkt);
    if (ret < 0)
    {
        if (AVERROR (EAGAIN) != ret)
        {
            /*if the return is EAGAIN,we need to try more times */
            dt_warning (TAG, "[%s:%d]av_read_frame return (%d)\n", __FUNCTION__, __LINE__, ret);

            if (AVERROR_EOF != ret)
                return DTERROR_READ_FAILED;
            else
                return DTERROR_READ_EOF;
        }
        return DTERROR_READ_AGAIN;
    }
    //read frame ok
    if (has_video && cur_vidx == avpkt.stream_index)
        frame->type = AVMEDIA_TYPE_VIDEO;
    else if (has_audio && cur_aidx == avpkt.stream_index)
        frame->type = AVMEDIA_TYPE_AUDIO;
    else if (has_sub && cur_sidx == avpkt.stream_index)
        frame->type = AVMEDIA_TYPE_SUBTITLE;
    else
    {
        frame->type = AVMEDIA_TYPE_UNKNOWN;
        av_free_packet (&avpkt);
        return DTERROR_READ_AGAIN; // need to read again
    }

    //setup frame
    frame->data = avpkt.data;
    frame->size = avpkt.size;
    frame->pts = pts_exchange (&avpkt, media_info);
    frame->dts = avpkt.dts;
    frame->duration = avpkt.duration;
    dt_debug (TAG, "read ok,frame size:%d %02x %02x %02x %02x addr:%p\n", frame->size, frame->data[0], frame->data[1], frame->data[2], frame->data[3], frame->data);
    dt_debug (TAG, "SIDE_DATA_ELEMENT:%d \n", avpkt.side_data_elems);

    return DTERROR_NONE;
}
Exemple #3
0
int main()
{
    dt_get_log_level();
    dt_info("TEST", "this is info level test \n");
    dt_error("TEST", "this is error level test \n");
    dt_debug("TEST", "this is debug level test \n");
    dt_warning("TEST", "this is warnning level test \n");
    dt_set_log_level(1);
    dt_info("TEST", "this is info level test \n");
    dt_error("TEST", "this is error level test \n");
    dt_debug("TEST", "this is debug level test \n");
    dt_warning("TEST", "this is warning level test \n");
    return;
}
Exemple #4
0
int64_t video_get_first_pts (dtvideo_context_t * vctx)
{
    if (vctx->video_status != VIDEO_STATUS_INITED)
        return -1;
    dt_debug (TAG, "fitst vpts:%lld \n", vctx->video_dec.pts_first);
    return vctx->video_dec.pts_first;
}
Exemple #5
0
int dthost_init (void **host_priv, dthost_para_t * para)
{
    int ret = 0;
    if (*host_priv)
    {
        dt_error (TAG, "[%s:%d] host_priv is Null\n", __FUNCTION__, __LINE__);
        ret = -1;
        goto ERR0;
    }
    dthost_context_t *hctx = malloc (sizeof (dthost_context_t));
    if (!hctx)
    {
        dt_info (TAG, "[%s:%d] dthost_context_t malloc failed\n", __FUNCTION__, __LINE__);
        ret = -1;
        goto ERR0;
    }
    dt_debug (TAG, "hctx :%p \n", hctx);
    memset (hctx, 0, sizeof (dthost_context_t));
    memcpy (&hctx->para, para, sizeof (dthost_para_t));
    ret = host_init (hctx);
    if (ret < 0)
    {
        dt_error (TAG, "[%s:%d] dthost_init failed\n", __FUNCTION__, __LINE__);
        ret = -1;
        goto ERR1;
    }
    *host_priv = (void *) hctx;
    return ret;
ERR1:
    free (hctx);
ERR0:
    return ret;
}
Exemple #6
0
int dt_send_event (event_t * event)
{
    dt_server_mgt_t *mgt = &server_mgt;
    event_server_t *server_hub = mgt->server;
    if (!server_hub)
    {
        dt_error (TAG, "EVENT SEND FAILED \n");
        return -1;
    }
    dt_lock (&server_hub->event_lock);
    if (server_hub->event_count == 0)
        server_hub->event = event;
    else
    {
        event_t *entry = server_hub->event;
        while (entry->next)
            entry = entry->next;
        entry->next = event;

    }
    server_hub->event_count++;
    dt_unlock (&server_hub->event_lock);
    dt_debug (TAG, "EVENT:%d SEND OK, event count:%d \n", event->type, server_hub->event_count);
    return 0;
}
Exemple #7
0
int dthost_start (void *host_priv)
{
    int ret = 0;
    dthost_context_t *hctx = (dthost_context_t *) host_priv;
    dt_debug (TAG, "hctx :%p \n", hctx);
    ret = host_start (hctx);
    return ret;
}
Exemple #8
0
static void audio_convert(dtaudio_decoder_t *decoder, AVFrame * dst,
                          AVFrame * src)
{
    int nb_sample;
    int dst_buf_size;
    int out_channels;
    //for audio post processor
    //struct SwsContext *m_sws_ctx = NULL;
    struct SwrContext *m_swr_ctx = NULL;
    //ResampleContext *m_resample_ctx=NULL;
    enum AVSampleFormat src_fmt = avctxp->sample_fmt;
    enum AVSampleFormat dst_fmt = AV_SAMPLE_FMT_S16;

    dst->linesize[0] = src->linesize[0];
    *dst = *src;

    dst->data[0] = NULL;
    out_channels = decoder->para.dst_channels;
    nb_sample = frame->nb_samples;
    dst_buf_size = nb_sample * av_get_bytes_per_sample(dst_fmt) * out_channels;
    dst->data[0] = (uint8_t *) av_malloc(dst_buf_size);

    avcodec_fill_audio_frame(dst, out_channels, dst_fmt, dst->data[0], dst_buf_size,
                             0);
    dt_debug(TAG, "SRCFMT:%d dst_fmt:%d \n", src_fmt, dst_fmt);
    /* resample toAV_SAMPLE_FMT_S16 */
    if (src_fmt != dst_fmt || out_channels != decoder->para.channels) {
        if (!m_swr_ctx) {
            uint64_t in_channel_layout = av_get_default_channel_layout(avctxp->channels);
            uint64_t out_channel_layout = av_get_default_channel_layout(out_channels);
            m_swr_ctx = swr_alloc_set_opts(NULL, out_channel_layout, dst_fmt,
                                           avctxp->sample_rate, in_channel_layout, src_fmt, avctxp->sample_rate, 0, NULL);
            swr_init(m_swr_ctx);
        }
        uint8_t **out = (uint8_t **) & dst->data;
        const uint8_t **in = (const uint8_t **) src->extended_data;
        if (m_swr_ctx) {
            int ret, out_count;
            out_count = nb_sample;
            ret = swr_convert(m_swr_ctx, out, out_count, in, nb_sample);
            if (ret < 0) {
                //set audio mute
                memset(dst->data[0], 0, dst_buf_size);
                printf("audio convert failed, set mute data\n");
            }
        }
    } else {                    // no need to convert ,just copy
        memcpy(dst->data[0], src->data[0], src->linesize[0]);
    }
    //free context
    if (m_swr_ctx != NULL) {
        swr_free(&m_swr_ctx);
    }
    //if(m_resample_ctx!=NULL)
    //    audio_resample_close(m_resample_ctx);
}
int64_t audio_decoder_get_pts (dtaudio_decoder_t * decoder)
{
    int64_t pts, delay_pts;
    int frame_num;
    int channels, sample_rate, bps;

    int len;
    float pts_ratio;
    dtaudio_context_t *actx = (dtaudio_context_t *) decoder->parent;
    dt_buffer_t *out = &actx->audio_decoded_buf;

    if (decoder->status == ADEC_STATUS_IDLE || decoder->status == ADEC_STATUS_EXIT)
        return -1;
    channels = decoder->aparam.dst_channels;
    sample_rate = decoder->aparam.samplerate;
    bps = decoder->aparam.bps;
    pts_ratio = (float) DTAUDIO_PTS_FREQ / sample_rate;
    pts = 0;
    if (-1 == decoder->pts_first)
        return -1;
    if (-1 == decoder->pts_current) //case 1 current_pts valid
    {
        //if(decoder->pts_last_valid)
        //    pts=decoder->pts_last_valid;
        //len = decoder->pts_cache_size + decoder->pts_buffer_size - out->level;
        len = decoder->pts_buffer_size;
        frame_num = (len) / (bps * channels / 8);
        pts += (frame_num) * pts_ratio;
        pts += decoder->pts_first;
        dt_debug (TAG, "[%s:%d] first_pts:%llu pts:%llu pts_s:%d frame_num:%d len:%d pts_ratio:%5.1f\n", __FUNCTION__, __LINE__, decoder->pts_first, pts, pts / 90000, frame_num, len, pts_ratio);
        return pts;
    }
    //case 2 current_pts invalid,calc pts mentally
    pts = decoder->pts_current;
    len = decoder->pts_buffer_size - out->level - decoder->pts_cache_size;
    frame_num = (len) / (bps * channels / 8);
    delay_pts = (frame_num) * pts_ratio;
    dt_debug (TAG, "[%s:%d] current_pts:%lld delay_pts:%lld  pts_s:%lld frame_num:%d pts_ratio:%f\n", __FUNCTION__, __LINE__, decoder->pts_current, delay_pts, pts / 90000, frame_num, pts_ratio);
    pts += delay_pts;
    if (pts < 0)
        pts = 0;
    return pts;
}
Exemple #10
0
static int stream_cache_seek (stream_wrapper_t * wrapper, int64_t pos, int whence)
{
    int ret = 0;
    cache_ctx_t *ctx = (cache_ctx_t *)wrapper->stream_priv;
    stream_cache_t *cache = ctx->cache;
    stream_wrapper_t *real_st = ctx->wrapper;
    stream_ctrl_t *info = &wrapper->info;
    info->eof_flag = 0;
    dt_info(TAG,"Enter cache seek \n");
    // ret stream size
    if(whence == AVSEEK_SIZE)
    {
        dt_debug(TAG,"REQUEST STREAM SIZE:%lld \n",info->stream_size);
        return info->stream_size;
    }
    int step;
    int orig;
    if(whence == SEEK_SET)
    {
        step = abs(info->cur_pos - pos);
        orig = (info->cur_pos > pos)?0:1;
    }
    if(whence == SEEK_CUR)
    {
        step = abs(pos);
        orig = (pos < 0)?0:1;
    }

    if(cache_seek(cache,step,orig) == 0) // seek in cache
    {
        dt_info(TAG,"cache seek success \n");
    }
    else // seek through real stream ops
    {
        pause_cache_thread(ctx);
        ret =real_st->seek(real_st,pos,whence); // fixme: seek maybe failed for online video
        if(ret != DTERROR_NONE)
        {
            dt_error(TAG,"SEEK FAILED \n");
        }
        cache_reset(ctx->cache);
        resume_cache_thread(ctx);
    }

    info->eof_flag = 0;
    if(whence == SEEK_SET)
        info->cur_pos = pos;
    if(whence == SEEK_CUR)
        info->cur_pos += pos;

    return ret;
}
Exemple #11
0
int dt_send_event_sync (event_t * event)
{
    dt_server_mgt_t *mgt = &server_mgt;
    event_server_t *server_hub = mgt->server;
    if (!server_hub)
    {
        dt_error (TAG, "EVENT SEND FAILED \n");
        return -1;
    }
    dt_transport_event (event, mgt);
    dt_debug (TAG, "EVENT:%d BYPASS SEND OK \n");
    return 0;
}
Exemple #12
0
static int ao_alsa_play(dtaudio_output_t *aout, uint8_t * buf, int size)
{
    alsa_ctx_t *ctx = (alsa_ctx_t *)wrapper->ao_priv;
    snd_pcm_t *alsa_handle = (snd_pcm_t *) ctx->handle;

    int bytes_per_sample = wrapper->para.bps * wrapper->para.dst_channels / 8;
    int num_frames = size / bytes_per_sample;
    snd_pcm_sframes_t res = 0;
    uint8_t *data = buf;

    if (!alsa_handle) {
        return -1;
    }

    if (num_frames == 0) {
        return 0;
    }

    if (ao_alsa_level(aout) >= ctx->buf_threshold) {
        dt_debug(TAG, "ALSA EXCEED THRESHOLD,size:%d  thres:%d \n", ao_alsa_level(aout),
                 ctx->buf_threshold);
        return 0;
    }

    res = snd_pcm_writei(alsa_handle, data, num_frames);
    if (res == -EINTR) {
        dt_info(TAG, "ALSA HAS NO SPACE, WRITE AGAIN\n");
        return res;
    }
    if (res == -ESTRPIPE) {
        snd_pcm_resume(alsa_handle);
        return res;
    }
    if (res == -EBADFD) {
        snd_pcm_reset(alsa_handle);
        return res;
    }
    if (res < 0) {
        snd_pcm_prepare(alsa_handle);
        dt_info(TAG, "snd pcm write failed prepare!\n");
        return -1;
        //goto rewrite;
    }
    if (res < num_frames) {
        data += res * bytes_per_sample;
        num_frames -= res;
        //goto rewrite;
    }
    return res * bytes_per_sample;
}
static int stream_ffmpeg_seek(stream_wrapper_t * wrapper, int64_t pos,
                              int whence)
{
    AVIOContext *ctx = (AVIOContext *)wrapper->stream_priv;
    stream_ctrl_t *info = &wrapper->info;
    info->eof_flag = 0;

    if (whence == AVSEEK_SIZE) {
        dt_debug(TAG, "REQUEST STREAM SIZE:%lld \n", info->stream_size);
        return avio_size(ctx);
    }

    return avio_seek(ctx, pos, whence);
}
Exemple #14
0
static int stream_file_read(stream_wrapper_t * wrapper, uint8_t *buf, int len)
{
    file_ctx_t *ctx = (file_ctx_t *)wrapper->stream_priv;
    stream_ctrl_t *info = &wrapper->info;
    int r = read(ctx->fd, buf, len);
    dt_debug(TAG, "read %d byte \n", len);
    if (r > 0) {
        info->cur_pos += r;
    }
    if (r <= 0) {
        info->eof_flag = 1;
    }
    return (r <= 0) ? -1 : r;
}
Exemple #15
0
static int update_cb (player_state_t * state)
{
    if (state->cur_status == PLAYER_STATUS_EXIT)
    {
        dt_info (TAG, "RECEIVE EXIT CMD\n");
        ply_ctx.exit_flag = 1;
    }
    ply_ctx.cur_time = state->cur_time;
    ply_ctx.cur_time_ms = state->cur_time_ms;
    ply_ctx.duration = state->full_time;

    dt_debug (TAG, "UPDATECB CURSTATUS:%x \n", state->cur_status);
    dt_info(TAG,"CUR TIME %lld S  FULL TIME:%lld  \n",state->cur_time,state->full_time);
    return 0;
}
int packet_queue_put (dt_packet_queue_t * queue, dt_av_frame_t * frame)
{
    int ret;
    if (queue->nmutex)
        return -1;
    queue->nmutex = 1;
    if (frame->type == 0 && queue->size >= QUEUE_MAX_VBUF_SIZE)
    {
        dt_debug (TAG, "[%s:%d]type:%d(0 video 1 audio) packet queue exceed size\n", __FUNCTION__, __LINE__, frame->type);
        queue->nmutex = 0;
        return -2;
    }

    if (frame->type == 1 && queue->size >= QUEUE_MAX_ABUF_SIZE)
    {
        dt_debug (TAG, "[%s:%d]type:%d(0 video 1 audio) packet queue exceed size\n", __FUNCTION__, __LINE__, frame->type);
        queue->nmutex = 0;
        return -2;
    }
    ret = packet_queue_put_frame (queue, frame);
    queue->nmutex = 0;
    //printf("[%s:%d] packet queue in ok\n",__FUNCTION__,__LINE__);
    return ret;
}
Exemple #17
0
static int stream_file_seek(stream_wrapper_t * wrapper, int64_t pos, int whence)
{
    file_ctx_t *ctx = (file_ctx_t *)wrapper->stream_priv;
    stream_ctrl_t *info = &wrapper->info;
    info->eof_flag = 0;
    if (whence == AVSEEK_SIZE) {
        dt_debug(TAG, "REQUEST STREAM SIZE:%lld \n", info->stream_size);
        return info->stream_size;
    }

    if (lseek(ctx->fd, pos, whence) < 0) {
        return -1;
    }
    if (whence == SEEK_SET) {
        info->cur_pos = pos;
    }
    if (whence == SEEK_CUR) {
        info->cur_pos += pos;
    }
    return DTERROR_NONE;
}
Exemple #18
0
static int stream_ffmpeg_seek (stream_wrapper_t * wrapper, int64_t pos, int whence)
{
    AVIOContext *ctx = (AVIOContext *)wrapper->stream_priv;
    stream_ctrl_t *info = &wrapper->info;
    info->eof_flag = 0;    
    if(whence == AVSEEK_SIZE)
    {
        dt_debug(TAG,"REQUEST STREAM SIZE:%lld \n",info->stream_size);
        return info->stream_size;
    }

    if (avio_seek(ctx, pos, whence) < 0) {
        info->eof_flag = 1;
        return -1;
    }
    if(whence == SEEK_SET)
        info->cur_pos = pos;
    if(whence == SEEK_CUR)
        info->cur_pos += pos;
    return DTERROR_NONE;
}
Exemple #19
0
static int64_t ao_alsa_get_latency(dtaudio_output_t *aout)
{
    alsa_ctx_t *ctx = (alsa_ctx_t *)wrapper->ao_priv;

    unsigned int sample_num;
    uint64_t latency, latency_s;

    ctx->buf_level = ctx->buf_size - ao_alsa_space(ctx);
    sample_num = ctx->buf_level / (wrapper->para.dst_channels * wrapper->para.bps /
                                   8);

    float pts_ratio = (double) 90000 / wrapper->para.samplerate;
    latency = (sample_num * pts_ratio);
    latency_s = latency / 90000;

    dt_debug(TAG,
             "[%s:%d]==alsa_level:%d thres:%d sample_num:%d buffersize:%d latency:%llu latency_s:%llu \n",
             __FUNCTION__, __LINE__, ctx->buf_level, ctx->buf_threshold, sample_num,
             ctx->buf_size, latency, latency_s);
    return latency;
}
Exemple #20
0
int video_drop (dtvideo_context_t * vctx, int64_t target_pts)
{
    int64_t diff = target_pts - video_get_first_pts (vctx);
    int diff_time = diff / 90;
    if (diff_time > AVSYNC_DROP_THRESHOLD)
    {
        dt_info (TAG, "diff time exceed %d ms, do not drop!\n", diff_time);
        return 0;
    }
    dt_info (TAG, "[%s:%d]target pts:%lld \n", __FUNCTION__, __LINE__, target_pts);
    AVPicture_t *pic = NULL;
    int64_t cur_pts = video_get_first_pts (vctx);
    int drop_count = 300;
    do
    {
        pic = dtvideo_output_read ((void *) vctx);
        if (!pic)
        {
            if (drop_count-- == 0)
            {
                dt_info (TAG, "3s read nothing, quit drop video\n");
                break;
            }
            usleep (100);
            continue;
        }
        drop_count = 300;
        cur_pts = pic->pts;
        dt_debug (TAG, "read pts:%lld \n", pic->pts);
        free (pic);
        pic = NULL;
        if (cur_pts > target_pts)
            break;
    }
    while (1);
    dt_info (TAG, "video drop finish,drop count:%d \n", drop_count);
    vctx->current_pts = cur_pts;
    dtvideo_update_pts ((void *) vctx);
    return 0;
}
Exemple #21
0
static int stream_cache_read (stream_wrapper_t * wrapper,uint8_t *buf,int len)
{
    cache_ctx_t *ctx = (cache_ctx_t *)wrapper->stream_priv;
    stream_ctrl_t *info = &wrapper->info;
    stream_cache_t *cache = ctx->cache;
    
    int level = cache_level(cache);
    if(level == 0)
        return 0;
    int r = cache_read(cache,buf,len);
    dt_debug(TAG,"read %d byte \n",r);
    if(r>0)
        info->cur_pos += r;
    if(r<=0 && ctx->cache_flag == ST_FLAG_EOF)
    {
        if(ctx->cache_flag == ST_FLAG_EOF)
            info->eof_flag = 1;
        else
            r=0;
    }
    return r;
}
static int calc_cur_time(dtplayer_context_t * dtp_ctx,
                         host_state_t * host_state)
{
    player_ctrl_t *ctrl_info = &dtp_ctx->ctrl_info;
    dtp_state_t *play_stat = &dtp_ctx->state;

    int has_audio = ctrl_info->has_audio;
    int has_video = ctrl_info->has_video;
    int64_t apts_first = host_state->pts_audio_first;
    int64_t vpts_first = host_state->pts_video_first;

    if (ctrl_info->start_time > 0) {
        play_stat->start_time = ctrl_info->start_time;
        dt_info(TAG, "START TIME:0x%llx \n", ctrl_info->start_time);
    }

    if (ctrl_info->first_time == -1) {
        if (has_audio && has_video) {
            if (PTS_VALID(apts_first) && PTS_VALID(vpts_first)) {
                if (llabs(apts_first - vpts_first) < FIRST_TIME_DIFF_MAX) {
                    ctrl_info->first_time = (apts_first > vpts_first) ? vpts_first : apts_first;
                } else {
                    if (llabs(vpts_first - play_stat->start_time) < FIRST_TIME_DIFF_MAX) {
                        ctrl_info->first_time = vpts_first;
                    } else if (llabs(apts_first - play_stat->start_time) < FIRST_TIME_DIFF_MAX) {
                        ctrl_info->first_time = apts_first;
                    }
                }
            } else if (PTS_VALID(apts_first)) {
                ctrl_info->first_time = apts_first;
            } else if (PTS_VALID(vpts_first)) {
                ctrl_info->first_time = vpts_first;
            }
        } else {
            ctrl_info->first_time = host_state->sys_time_current;
        }

        dt_info(TAG,
                "[apts_first:0x%llx] [vpts_first:0x%llx] [starttime:0x%llx] -> first_time:0x%llx \n",
                apts_first, vpts_first, play_stat->start_time, ctrl_info->first_time);
    }

    if (play_stat->full_time == 0) {
        play_stat->full_time = dtp_ctx->media_info->duration;
    }

    int64_t sys_time_start = host_state->sys_time_start;
    int64_t sys_time_current = host_state->sys_time_current;
    int64_t pts_audio_current = host_state->pts_audio_current;
    int     audio_discontinue_flag = host_state->audio_discontinue_flag;
    int64_t audio_discontinue_point = host_state->audio_discontinue_point;
    int64_t pts_video_current = host_state->pts_video_current;
    int     video_discontinue_flag = host_state->video_discontinue_flag;
    int64_t video_discontinue_point = host_state->video_discontinue_point;

    int avdiff = llabs(pts_audio_current - pts_video_current) / DT_PTS_FREQ_MS;
    int64_t sys_time = -1;
    int64_t start_time = ctrl_info->first_time;
    int discontinue_flag = 0;

    if (has_audio && has_video) {
        if (audio_discontinue_flag
            && video_discontinue_flag) { // both discontinue occured
            play_stat->discontinue_point_ms = play_stat->cur_time_ms;
            if (avdiff < AVSYNC_THRESHOLD_MAX) {
                player_host_set_info(dtp_ctx, HOST_CMD_SET_DISCONTINUE_FLAG,
                                     (unsigned long)(&discontinue_flag));
            }
            sys_time = pts_video_current;
            if (audio_discontinue_point > 0) {
                start_time = audio_discontinue_point;
            }
        } else if (audio_discontinue_flag) { // only audio discontinue occured
            if (avdiff < AVSYNC_THRESHOLD_MAX) {
                player_host_set_info(dtp_ctx, HOST_CMD_SET_DISCONTINUE_FLAG,
                                     (unsigned long)(&discontinue_flag));
            }
            sys_time = pts_video_current;
            if (video_discontinue_point > 0) {
                start_time = video_discontinue_point;
            }
        } else if (video_discontinue_flag) { // only video discontinue occured
            if (avdiff < AVSYNC_THRESHOLD_MAX) {
                player_host_set_info(dtp_ctx, HOST_CMD_SET_DISCONTINUE_FLAG,
                                     (unsigned long)(&discontinue_flag));
            }
            sys_time = pts_audio_current;
            if (audio_discontinue_point > 0) {
                start_time = audio_discontinue_point;
            }
        } else {
            if (PTS_VALID(vpts_first)) {
                sys_time = pts_video_current;
            } else {
                sys_time = pts_audio_current;
            }
            if (audio_discontinue_point > 0) {
                start_time = audio_discontinue_point;
            }
        }
    } else {
        sys_time = sys_time_current;
        start_time = sys_time_start;
    }

    sys_time += play_stat->discontinue_point_ms * DT_PTS_FREQ_MS;
    sys_time -= start_time;
    play_stat->cur_time = sys_time / DT_PTS_FREQ;
    play_stat->cur_time_ms = sys_time / DT_PTS_FREQ_MS;

    dt_debug(TAG, "---------------------------------------------\n");
    dt_debug(TAG,
             "apts:%llx audio_discontinue_flag:%d audio_discontinue_point:%llx\n",
             pts_audio_current, audio_discontinue_flag, audio_discontinue_point);
    dt_debug(TAG,
             "vpts:%llx video_discontinue_flag:%d video_discontinue_point:%llx\n",
             pts_video_current, video_discontinue_flag, video_discontinue_point);
    dt_debug(TAG, "sys_time:%llx start_time:%llx\n",
             (sys_time - play_stat->discontinue_point_ms * DT_PTS_FREQ_MS), start_time);
    dt_debug(TAG, "---------------------------------------------\n");

    return 0;
}
Exemple #23
0
int demuxer_open(dtdemuxer_context_t * dem_ctx)
{
    int ret = 0;
    /* open stream */
    dtstream_para_t para;
    para.stream_name = dem_ctx->file_name;
    ret = dtstream_open(&dem_ctx->stream_priv, &para, dem_ctx);
    if (ret != DTERROR_NONE) {
        dt_error(TAG, "stream open failed \n");
        goto DEMUXER_SELECT;
    }

    dt_info(TAG, "probe enable start \n");
    int probe_enable = dtp_setting.demuxer_probe;
    int probe_size = dtstream_local(dem_ctx->stream_priv) ? PROBE_LOCAL_SIZE : PROBE_STREAM_SIZE;
    dt_info(TAG, "probe enable:%d \n", probe_enable);
    dt_info(TAG, "probe size:%d \n", probe_size);

    if (probe_enable) {
        int64_t old_pos = dtstream_tell(dem_ctx->stream_priv);
        dt_info(TAG, "old:%lld \n", old_pos);
        ret = buf_init(&dem_ctx->probe_buf, probe_size);
        if (ret < 0) {
            return -1;
        }
        //buffer data for probe
        int total = probe_size;
        int rtotal = 0;
        int rlen = 0;
        int retry = 100;
        const int read_once = 10240;
        uint8_t tmp_buf[read_once];
        do {

            rlen = dtstream_read(dem_ctx->stream_priv, tmp_buf, read_once);
            if (rlen < 0) { // read eof
                if ((probe_size - total) == 0) {
                    return -1;
                } else {
                    break;
                }
            }
            if (rlen > 0) {
                retry = 20;
            } else {
                usleep(100000); // total time :20 * 100000 = 10s
                retry--;
            }
            if (retry == 0) {
                dt_info(TAG, "retry 100 times, total:%d  \n", total);
                if ((probe_size - total) == 0) {
                    return -1;
                } else {
                    break;
                }
            }
            dt_debug(TAG, "total:%d rtotal:%d rlen:%d \n", total, rtotal, rlen);
            if (rlen == 0) {
                continue;
            }
            if (buf_put(&dem_ctx->probe_buf, tmp_buf, rlen) == 0) { // full
                break;
            }
            rtotal += rlen;
            total -= rlen;
            if (total == 0) {
                break;
            }
        } while (1);

        dt_info(TAG, "buffered %d bytes for probe \n", probe_size - total);
        ret = dtstream_seek(dem_ctx->stream_priv, old_pos, SEEK_SET);
        dt_info(TAG, "seek back to:%lld ret:%d \n", old_pos, ret);
    }
DEMUXER_SELECT:
    /* select demuxer */
    if (demuxer_select(dem_ctx) == -1) {
        dt_error(TAG, "select demuxer failed \n");
        return -1;
    }
    demuxer_wrapper_t *wrapper = dem_ctx->demuxer;
    wrapper->parent = dem_ctx;
    ret = wrapper->open(wrapper);
    if (ret < 0) {
        dt_error(TAG, "demuxer open failed\n");
        return -1;
    }
    dt_info(TAG, "demuxer open ok\n");
    dtp_media_info_t *info = &(dem_ctx->media_info);
    wrapper->setup_info(wrapper, info);
    dump_media_info(info);
    dt_info(TAG, "demuxer setup info ok\n");
    return 0;
}
static void *video_decode_loop(void *arg)
{
    dt_av_pkt_t frame;
    dtvideo_decoder_t *decoder = (dtvideo_decoder_t *) arg;
    vd_wrapper_t *wrapper = decoder->wrapper;
    vd_statistics_info_t *p_vd_statistics_info = &decoder->statistics_info;
    dtvideo_context_t *vctx = (dtvideo_context_t *) decoder->parent;
    queue_t *picture_queue = vctx->vo_queue;
    /*used for decode */
    dt_av_frame_t *picture = NULL;
    int ret;
    dt_info(TAG, "[%s:%d] start decode loop \n", __FUNCTION__, __LINE__);

    do {
        //exit check before idle, maybe recieve exit cmd in idle status
        if (decoder->status == VDEC_STATUS_EXIT) {
            dt_info(TAG, "[%s:%d] receive decode loop exit cmd \n", __FUNCTION__, __LINE__);
            break;
        }

        if (decoder->status == VDEC_STATUS_IDLE) {
            dt_info(TAG, "[%s:%d] Idle status ,please wait \n", __FUNCTION__, __LINE__);
            usleep(100);
            continue;
        }

        /*read frame */
        if (!decoder->parent) {
            usleep(100);
            continue;
        }

        if (picture_queue->length >= VIDEO_OUT_MAX_COUNT) {
            //vo queue full
            usleep(1000);
            continue;
        }
        ret = dtvideo_read_frame(decoder->parent, &frame);
        if (ret < 0) {
            dt_usleep(1000);
            if (decoder->pts_first == DT_NOPTS_VALUE) {
                continue;
            }
            //no data left, maybe eof, need to flush left data
            memset(&frame, 0, sizeof(dt_av_pkt_t));
            dt_debug(TAG, "[%s:%d] no video frame left, flush left frames \n", __FUNCTION__,
                     __LINE__);
        }
        /*read one frame,enter decode frame module */
        //will exec once for one time
        ret = wrapper->decode_frame(decoder, &frame, &picture);
        if (ret <= 0) {
            decoder->decode_err_cnt++;
            dt_debug(TAG, "[%s:%d]decode failed \n", __FUNCTION__, __LINE__);
            picture = NULL;
            //usleep(10000);
            goto DECODE_END;
        }
        if (!picture) {
            goto DECODE_END;
        }

        // statistics collection
        {
            p_vd_statistics_info->decoded_frame_count++;
        }
        decoder->frame_count++;
        //Got one frame
        //update current pts, clear the buffer size
        if (PTS_VALID(picture->pts)) {
            picture->pts = pts_exchange(decoder, picture->pts);
        }
        if (decoder->first_frame_decoded == 0 && PTS_INVALID(decoder->pts_first)) {
            decoder->pts_first = decoder->pts_current = picture->pts;
            decoder->first_frame_decoded = 1;
            dt_info(TAG, "[%s:%d]first frame decoded ok, pts:0x%llx dts:0x%llx\n",
                    __FUNCTION__, __LINE__, picture->pts, picture->dts);
        } else {
            if (pts_mode || PTS_INVALID(picture->pts)) {
                int fps = decoder->para.fps;
                int dur_inc = (int)((double)90000 / fps);
                picture->pts = decoder->pts_current + dur_inc;
                decoder->pts_current = picture->pts;
                dt_debug(TAG, "vpts inc itself. pts_mode:%d pts:0x%llx inc:%d\n", pts_mode,
                         picture->pts, dur_inc);
            }
        }

        /*queue in */
        queue_push_tail(picture_queue, picture);
        picture = NULL;
DECODE_END:
        //we successfully decodec one frame
        if (frame.data) {
            if (frame.data) {
                free(frame.data);
            }
            frame.data = NULL;
            frame.size = 0;
            frame.pts = -1;
        }
    } while (1);
    dt_info(TAG, "[file:%s][%s:%d]decoder loop thread exit ok\n", __FILE__,
            __FUNCTION__, __LINE__);

    pthread_exit(NULL);
    return NULL;
}
Exemple #25
0
/***********************************************************************
**
** capability check
**
***********************************************************************/
static int opengl_vf_capable(vf_cap_t cap)
{
    int opengl_cap = VF_CAP_COLORSPACE_CONVERT | VF_CAP_CLIP;
    dt_debug(TAG, "request cap: %x , %s support:%x \n", cap, "opengl vf", ffmpeg_cap);
    return cap & ffmpeg_cap; 
}
static void *audio_decode_loop (void *arg)
{
    int ret;
    dtaudio_decoder_t *decoder = (dtaudio_decoder_t *) arg;
    dtaudio_para_t *para = &decoder->aparam;
    dt_av_frame_t frame;
    ad_wrapper_t *wrapper = decoder->wrapper;
    dtaudio_context_t *actx = (dtaudio_context_t *) decoder->parent;
    dt_buffer_t *out = &actx->audio_decoded_buf;
    int declen, fill_size;
    
    //for some type audio, can not read completly frame 
    uint8_t *frame_data = NULL; // point to frame start
    uint8_t *rest_data = NULL;
    int frame_size = 0;
    int rest_size = 0;

    int used;                   // used size after every decode ops

    adec_ctrl_t *pinfo = &decoder->info;
    memset(pinfo,0,sizeof(*pinfo));
    pinfo->channels = para->channels;
    pinfo->samplerate = para->samplerate;
    pinfo->outptr = malloc(MAX_ONE_FRAME_OUT_SIZE);
    pinfo->outsize = MAX_ONE_FRAME_OUT_SIZE;

    dt_info (TAG, "[%s:%d] AUDIO DECODE START \n", __FUNCTION__, __LINE__);
    do
    {
        //maybe receive exit cmd in idle status, so exit prior to idle
        if (decoder->status == ADEC_STATUS_EXIT) 
        {
            dt_debug (TAG, "[%s:%d] receive decode loop exit cmd \n", __FUNCTION__, __LINE__);
            if (frame_data)
                free (frame_data);
            if (rest_data)
                free (rest_data);
            break;
        }

        if (decoder->status == ADEC_STATUS_IDLE)
        {
            dt_info (TAG, "[%s:%d] Idle status ,please wait \n", __FUNCTION__, __LINE__);
            usleep (100);
            continue;
        }

        /*read frame */
        if (!decoder->parent)
        {
            usleep (10000);
            dt_info (TAG, "[%s:%d] decoder parent is NULL \n", __FUNCTION__, __LINE__);
            continue;
        }
        ret = audio_read_frame (decoder->parent, &frame);
        if (ret < 0 || frame.size <= 0)
        {
            usleep (1000);
            dt_debug (TAG, "[%s:%d] dtaudio decoder loop read frame failed \n", __FUNCTION__, __LINE__);
            continue;
        }
        //read ok,update current pts, clear the buffer size
        if (frame.pts >= 0)
        {
            if (decoder->pts_first == -1)
            {
                if(frame.pts == DT_NOPTS_VALUE)
                    frame.pts = 0;
                decoder->pts_first = pts_exchange (decoder, frame.pts);
                dt_info (TAG, "first frame pts:%lld dts:%lld duration:%d size:%d\n", decoder->pts_first, frame.dts, frame.duration, frame.size);
            }
            decoder->pts_current = pts_exchange (decoder, frame.pts);
            dt_debug (TAG, "pkt pts:%lld current:%lld duration:%d pts_s:%lld dts:%lld buf_size:%d \n", frame.pts, decoder->pts_current, frame.duration, frame.pts / 90000, frame.dts, decoder->pts_buffer_size);
            decoder->pts_last_valid = decoder->pts_current;
            decoder->pts_buffer_size = 0;
        }
        //repack the frame
        if (frame_data)
        {
            free (frame_data);
            frame_data = NULL;
            frame_size = 0;
        }
        if (rest_size)
            frame_data = malloc (frame.size + rest_size);
        else
            frame_data = frame.data;
        if (!frame_data)
        {
            dt_error (TAG, "malloc audio frame failed ,we will lost one frame\n");
            if (rest_data)
                free (rest_data);
            rest_size = 0;
            continue;
        }

        if (rest_size)          // no rest data
        {
            dt_debug (TAG, "left %d byet last time\n", rest_size);
            memcpy (frame_data, rest_data, rest_size);
            free (rest_data);
            rest_data = NULL;
            memcpy (frame_data + rest_size, frame.data, frame.size);
        }

        frame_size = frame.size + rest_size;
        rest_size = 0;
        used = 0;
        declen = 0;

        pinfo->inptr = frame_data;
        pinfo->inlen = frame_size;
        pinfo->outlen = 0; 

        //free pkt
        frame.data = NULL;
        frame.size = 0;

      DECODE_LOOP:
        if (decoder->status == ADEC_STATUS_EXIT)
        {
            dt_info (TAG, "[%s:%d] receive decode loop exit cmd \n", __FUNCTION__, __LINE__);
            if (frame_data)
                free (frame_data);
            break;
        }
        /*decode frame */
        pinfo->consume = declen;
        used = wrapper->decode_frame (wrapper, pinfo);
        if (used < 0)
        {
            decoder->decode_err_cnt++;
            /*
             * if decoder is ffmpeg,do not restore data if decode failed
             * if decoder is not ffmpeg, restore raw stream packet if decode failed
             * */
            if (!strcmp (wrapper->name, "ffmpeg audio decoder"))
            {
                dt_error (TAG, "[%s:%d] ffmpeg failed to decode this frame, just break\n", __FUNCTION__, __LINE__);
                decoder->decode_offset += pinfo->inlen;
            }
            continue;
        }
        else if (used == 0 && pinfo->outlen == 0) // used == 0 && out == 0 means need more data
        {
            //maybe need more data
            rest_data = malloc (pinfo->inlen);
            if (rest_data == NULL)
            {
                dt_error ("[%s:%d] rest_data malloc failed\n", __FUNCTION__, __LINE__);
                rest_size = 0;  //skip this frame
                continue;
            }
            memcpy (rest_data, pinfo->inptr, pinfo->inlen);
            rest_size = pinfo->inlen;
            dt_info (TAG, "Maybe we need more data\n");
            continue;
        }
        declen += used;
        pinfo->inlen -= used;
        decoder->decode_offset += used;
        decoder->pts_cache_size = pinfo->outlen;
        decoder->pts_buffer_size += pinfo->outlen;
        if (pinfo->outlen == 0)      //get no data, maybe first time for init
            dt_info (TAG, "GET NO PCM DECODED OUT,used:%d \n",used);

        fill_size = 0;
      REFILL_BUFFER:
        if (decoder->status == ADEC_STATUS_EXIT)
            goto EXIT;
        /*write pcm */
        if (buf_space (out) < pinfo->outlen)
        {
            dt_debug (TAG, "[%s:%d] output buffer do not left enough space ,space=%d level:%d outsie:%d \n", __FUNCTION__, __LINE__, buf_space (out), buf_level (out), pinfo->outlen);
            usleep (1000);
            goto REFILL_BUFFER;
        }
        ret = buf_put (out, pinfo->outptr + fill_size, pinfo->outlen);
        fill_size += ret;
        pinfo->outlen -= ret;
        decoder->pts_cache_size = pinfo->outlen;
        if (pinfo->outlen > 0)
            goto REFILL_BUFFER;

        if (pinfo->inlen)
            goto DECODE_LOOP;
    }
    while (1);
  EXIT:
    dt_info (TAG, "[file:%s][%s:%d]decoder loop thread exit ok\n", __FILE__, __FUNCTION__, __LINE__);
    /* free adec_ctrl_t buf */
    if(pinfo->outptr)
        free(pinfo->outptr);
    pinfo->outlen = pinfo->outsize = 0;
    pthread_exit (NULL);
    return NULL;
}
static void *video_decode_loop (void *arg)
{
    dt_av_frame_t frame;
    dtvideo_decoder_t *decoder = (dtvideo_decoder_t *) arg;
    vd_wrapper_t *wrapper = decoder->wrapper;
    dtvideo_context_t *vctx = (dtvideo_context_t *) decoder->parent;
    queue_t *picture_queue = vctx->vo_queue;
    /*used for decode */
    AVPicture_t *picture = NULL;
    int ret;
    dt_info (TAG, "[%s:%d] start decode loop \n", __FUNCTION__, __LINE__);

    do
    {
        if (decoder->status == VDEC_STATUS_IDLE)
        {
            dt_info (TAG, "[%s:%d] Idle status ,please wait \n", __FUNCTION__, __LINE__);
            usleep (100);
            continue;
        }
        if (decoder->status == VDEC_STATUS_EXIT)
        {
            dt_info (TAG, "[%s:%d] receive decode loop exit cmd \n", __FUNCTION__, __LINE__);
            break;
        }

        /*read frame */
        if (!decoder->parent)
        {
            usleep (100);
            continue;
        }

        if (picture_queue->length >= VIDEO_OUT_MAX_COUNT)
        {
            //vo queue full
            usleep (1000);
            continue;
        }
        ret = dtvideo_read_frame (decoder->parent, &frame);
        if (ret < 0)
        {
            usleep (100);
            dt_debug (TAG, "[%s:%d] dtaudio decoder loop read frame failed \n", __FUNCTION__, __LINE__);
            continue;
        }
        /*read one frame,enter decode frame module */
        //will exec once for one time
        ret = wrapper->decode_frame (wrapper, &frame, &picture);
        if (ret <= 0)
        {
            decoder->decode_err_cnt++;
            dt_debug (TAG, "[%s:%d]decode failed \n", __FUNCTION__, __LINE__);
            picture = NULL;
            goto DECODE_END;
        }
        if (!picture)
            goto DECODE_END;
        decoder->frame_count++;
        //Got one frame
        //picture->pts = frame.pts;
        
        //update current pts, clear the buffer size
        if (frame.pts >= 0 && decoder->pts_first == -1)
        {
            //we will use first pts to estimate pts
            dt_info (TAG, "[%s:%d]first frame decoded ok, pts:0x%x dts:0x%x duration:%d size:%d\n", __FUNCTION__, __LINE__, frame.pts, frame.dts, frame.duration, frame.size);
            decoder->pts_first = pts_exchange (decoder, picture->pts);
            decoder->pts_current = decoder->pts_first;
        }
        else
        {
            if(pts_mode)
            {
                int fps = decoder->para.fps;
                float dur_inc = 90000/fps;
                picture->pts = decoder->pts_current + dur_inc;
                decoder->pts_current = picture->pts; 
            }
        }

        /*queue in */
        queue_push_tail (picture_queue, picture);
        picture = NULL;
      DECODE_END:
        //we successfully decodec one frame
        if (frame.data)
        {
            free (frame.data);
            frame.data = NULL;
            frame.size = 0;
            frame.pts = -1;
        }
    }
    while (1);
    dt_info (TAG, "[file:%s][%s:%d]decoder loop thread exit ok\n", __FILE__, __FUNCTION__, __LINE__);
    pthread_exit (NULL);
    return NULL;
}
Exemple #28
0
static void *cache_thread (cache_ctx_t * ctx)
{
    stream_wrapper_t *real_st = ctx->wrapper;
    stream_cache_t *cache = ctx->cache;

    uint8_t tmp_buf[READ_PER_TIME];
    int rlen = 0;
    int wlen = 0;

    do
    {
        usleep (10000);
        
        if(ctx->cache_flag == ST_FLAG_PAUSE)
            ctx->cache_status = ST_STATUS_PAUSED;

        if(ctx->cache_status == ST_STATUS_IDLE)
        {
            usleep(100000);
            continue;
        }

        if(ctx->cache_status == ST_STATUS_QUIT)
            goto QUIT;

        if(ctx->cache_status == ST_STATUS_PAUSED) // means clean everything
        {
            rlen = 0;
            ctx->cache_flag = ST_FLAG_NULL;
            ctx->cache_status = ST_STATUS_IDLE;
            dt_info(TAG,"paused thread, reset rlen and cache\n");
            continue;
        }

        if(rlen == 0)
        {
            rlen = real_st->read(real_st,tmp_buf,READ_PER_TIME);
            if(rlen < 0)
            {
                rlen = 0;
                ctx->cache_flag = ST_FLAG_EOF; // eof will enter IDLE MODE
                ctx->cache_status = ST_STATUS_IDLE;
                dt_info(TAG,"read eof, poll thread enter idle\n");
                continue;
            }
            dt_debug(TAG, "read ok size:%d \n",rlen);
        }
        
        if(cache_space(cache) < rlen)
        {
            usleep(100000);
            continue;
        }
        
        wlen = cache_write(cache,tmp_buf,rlen); 
        if (wlen == 0)
            continue;
        
        rlen -= wlen;
        if(rlen > 0) // here some err occured, skip this packet
        {
            rlen = 0;
            continue;
        }
    }
    while (1);
QUIT:
    dt_info (TAG, "cache thread quit ok\n");
    pthread_exit (NULL);
    return 0;
}
static void *audio_output_thread(void *args)
{
    dtaudio_output_t *ao = (dtaudio_output_t *) args;
    ao_wrapper_t *wrapper = ao->wrapper;

    int rlen, ret, wlen;
    rlen = ret = wlen = 0;

    dtaudio_para_t *para = &ao->para;
    int bytes_per_sample = para->data_width * para->dst_channels / 8;
    int unit_size = PCM_WRITE_SIZE * para->dst_samplerate * bytes_per_sample / 1000;
    unit_size = unit_size - unit_size % (4 * bytes_per_sample);
    uint8_t *buffer = malloc(unit_size);
    if (!buffer) { // err
        goto EXIT;
    }
    dt_info(TAG, "write :%d ms :%d bytes one time \n", PCM_WRITE_SIZE, unit_size);

    for (;;) {
        if (ao->status == AO_STATUS_EXIT) {
            goto EXIT;
        }
        if (ao->status == AO_STATUS_IDLE || ao->status == AO_STATUS_PAUSE) {
            usleep(100);
            continue;
        }

        /* update audio pts */
        audio_update_pts(ao->parent);

        /*read data from filter or decode buffer */
        if (rlen <= 0) {
            rlen = audio_output_read(ao->parent, buffer, unit_size);
            if (rlen <= 0) {
                dt_debug(LOG_TAG, "pcm read failed! \n");
                usleep(1000);
                continue;
            }
#ifdef DTAUDIO_DUMP_PCM
            FILE *fp = fopen("pcm.out", "ab+");
            int flen = 0;
            if (fp) {
                flen = fwrite(buffer, 1, rlen, fp);
                if (flen <= 0) {
                    dt_info(LOG_TAG, "pcm dump failed! \n");
                }
                fclose(fp);
            } else {
                dt_error(TAG, "pcm out open failed! \n");
            }
#endif
        }
        /*write to ao device */
        wlen = wrapper->write(wrapper, buffer, rlen);
        if (wlen <= 0) {
            usleep(1000);
            continue;
        }

        rlen -= wlen;
        if (rlen > 0) {
            memmove(buffer, buffer + wlen, rlen);
        }
        wlen = 0;
    }
EXIT:
    dt_info(LOG_TAG, "[file:%s][%s:%d]ao playback thread exit\n", __FILE__, __FUNCTION__, __LINE__);
    if (buffer) {
        free(buffer);
    }
    buffer = NULL;
    pthread_exit(NULL);
    return NULL;

}