Recherche avancée

Médias (1)

Mot : - Tags -/berlin

Autres articles (63)

  • Publier sur MédiaSpip

    13 juin 2013

    Puis-je poster des contenus à partir d’une tablette Ipad ?
    Oui, si votre Médiaspip installé est à la version 0.2 ou supérieure. Contacter au besoin l’administrateur de votre MédiaSpip pour le savoir

  • List of compatible distributions

    26 avril 2011, par

    The table below is the list of Linux distributions compatible with the automated installation script of MediaSPIP. Distribution nameVersion nameVersion number Debian Squeeze 6.x.x Debian Weezy 7.x.x Debian Jessie 8.x.x Ubuntu The Precise Pangolin 12.04 LTS Ubuntu The Trusty Tahr 14.04
    If you want to help us improve this list, you can provide us access to a machine whose distribution is not mentioned above or send the necessary fixes to add (...)

  • Encoding and processing into web-friendly formats

    13 avril 2011, par

    MediaSPIP automatically converts uploaded files to internet-compatible formats.
    Video files are encoded in MP4, Ogv and WebM (supported by HTML5) and MP4 (supported by Flash).
    Audio files are encoded in MP3 and Ogg (supported by HTML5) and MP3 (supported by Flash).
    Where possible, text is analyzed in order to retrieve the data needed for search engine detection, and then exported as a series of image files.
    All uploaded files are stored online in their original format, so you can (...)

Sur d’autres sites (9366)

  • Issues with Publishing and Subscribing Rates for H.264 Video Streaming over RabbitMQ

    7 octobre 2024, par Luis

    I am working on a project to stream an H.264 video file using RabbitMQ (AMQP protocol) and display it in a web application. The setup involves capturing video frames, encoding them, sending them to RabbitMQ, and then consuming and decoding them on the web application side using Flask and Flask-SocketIO.

    


    However, I am encountering performance issues with the publishing and subscribing rates in RabbitMQ. I cannot seem to achieve more than 10 messages per second. This is not sufficient for smooth video streaming.
I need help to diagnose and resolve these performance bottlenecks.

    


    Here is my code :

    


      

    • Video Capture and Publishing Script :
    • 


    


    # RabbitMQ setup
RABBITMQ_HOST = 'localhost'
EXCHANGE = 'DRONE'
CAM_LOCATION = 'Out_Front'
KEY = f'DRONE_{CAM_LOCATION}'
QUEUE_NAME = f'DRONE_{CAM_LOCATION}_video_queue'

# Path to the H.264 video file
VIDEO_FILE_PATH = 'videos/FPV.h264'

# Configure logging
logging.basicConfig(level=logging.INFO)

@contextmanager
def rabbitmq_channel(host):
    """Context manager to handle RabbitMQ channel setup and teardown."""
    connection = pika.BlockingConnection(pika.ConnectionParameters(host))
    channel = connection.channel()
    try:
        yield channel
    finally:
        connection.close()

def initialize_rabbitmq(channel):
    """Initialize RabbitMQ exchange and queue, and bind them together."""
    channel.exchange_declare(exchange=EXCHANGE, exchange_type='direct')
    channel.queue_declare(queue=QUEUE_NAME)
    channel.queue_bind(exchange=EXCHANGE, queue=QUEUE_NAME, routing_key=KEY)

def send_frame(channel, frame):
    """Encode the video frame using FFmpeg and send it to RabbitMQ."""
    ffmpeg_path = 'ffmpeg/bin/ffmpeg.exe'
    cmd = [
        ffmpeg_path,
        '-f', 'rawvideo',
        '-pix_fmt', 'rgb24',
        '-s', '{}x{}'.format(frame.shape[1], frame.shape[0]),
        '-i', 'pipe:0',
        '-f', 'h264',
        '-vcodec', 'libx264',
        '-pix_fmt', 'yuv420p',
        '-preset', 'ultrafast',
        'pipe:1'
    ]
    
    start_time = time.time()
    process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = process.communicate(input=frame.tobytes())
    encoding_time = time.time() - start_time
    
    if process.returncode != 0:
        logging.error("ffmpeg error: %s", err.decode())
        raise RuntimeError("ffmpeg error")
    
    frame_size = len(out)
    logging.info("Sending frame with shape: %s, size: %d bytes", frame.shape, frame_size)
    timestamp = time.time()
    formatted_timestamp = datetime.fromtimestamp(timestamp).strftime('%H:%M:%S.%f')
    logging.info(f"Timestamp: {timestamp}") 
    logging.info(f"Formatted Timestamp: {formatted_timestamp[:-3]}")
    timestamp_bytes = struct.pack('d', timestamp)
    message_body = timestamp_bytes + out
    channel.basic_publish(exchange=EXCHANGE, routing_key=KEY, body=message_body)
    logging.info(f"Encoding time: {encoding_time:.4f} seconds")

def capture_video(channel):
    """Read video from the file, encode frames, and send them to RabbitMQ."""
    if not os.path.exists(VIDEO_FILE_PATH):
        logging.error("Error: Video file does not exist.")
        return
    cap = cv2.VideoCapture(VIDEO_FILE_PATH)
    if not cap.isOpened():
        logging.error("Error: Could not open video file.")
        return
    try:
        while True:
            start_time = time.time()
            ret, frame = cap.read()
            read_time = time.time() - start_time
            if not ret:
                break
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            frame_rgb = np.ascontiguousarray(frame_rgb) # Ensure the frame is contiguous
            send_frame(channel, frame_rgb)
            cv2.imshow('Video', frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            logging.info(f"Read time: {read_time:.4f} seconds")
    finally:
        cap.release()
        cv2.destroyAllWindows()


    


      

    • the backend (flask) :
    • 


    


    app = Flask(__name__)
CORS(app)
socketio = SocketIO(app, cors_allowed_origins="*")

RABBITMQ_HOST = 'localhost'
EXCHANGE = 'DRONE'
CAM_LOCATION = 'Out_Front'
QUEUE_NAME = f'DRONE_{CAM_LOCATION}_video_queue'

def initialize_rabbitmq():
    connection = pika.BlockingConnection(pika.ConnectionParameters(RABBITMQ_HOST))
    channel = connection.channel()
    channel.exchange_declare(exchange=EXCHANGE, exchange_type='direct')
    channel.queue_declare(queue=QUEUE_NAME)
    channel.queue_bind(exchange=EXCHANGE, queue=QUEUE_NAME, routing_key=f'DRONE_{CAM_LOCATION}')
    return connection, channel

def decode_frame(frame_data):
    # FFmpeg command to decode H.264 frame data
    ffmpeg_path = 'ffmpeg/bin/ffmpeg.exe'
    cmd = [
        ffmpeg_path,
        '-f', 'h264',
        '-i', 'pipe:0',
        '-pix_fmt', 'bgr24',
        '-vcodec', 'rawvideo',
        '-an', '-sn',
        '-f', 'rawvideo',
        'pipe:1'
    ]
    process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    start_time = time.time()  # Start timing the decoding process
    out, err = process.communicate(input=frame_data)
    decoding_time = time.time() - start_time  # Calculate decoding time
    
    if process.returncode != 0:
        print("ffmpeg error: ", err.decode())
        return None
    frame_size = (960, 1280, 3)  # frame dimensions expected by the frontend
    frame = np.frombuffer(out, np.uint8).reshape(frame_size)
    print(f"Decoding time: {decoding_time:.4f} seconds")
    return frame

def format_timestamp(ts):
    dt = datetime.fromtimestamp(ts)
    return dt.strftime('%H:%M:%S.%f')[:-3]

def rabbitmq_consumer():
    connection, channel = initialize_rabbitmq()
    for method_frame, properties, body in channel.consume(QUEUE_NAME):
        message_receive_time = time.time()  # Time when the message is received

        # Extract the timestamp from the message body
        timestamp_bytes = body[:8]
        frame_data = body[8:]
        publish_timestamp = struct.unpack('d', timestamp_bytes)[0]

        print(f"Message Receive Time: {message_receive_time:.4f} ({format_timestamp(message_receive_time)})")
        print(f"Publish Time: {publish_timestamp:.4f} ({format_timestamp(publish_timestamp)})")

        frame = decode_frame(frame_data)
        decode_time = time.time() - message_receive_time  # Calculate decode time

        if frame is not None:
            _, buffer = cv2.imencode('.jpg', frame)
            frame_data = buffer.tobytes()
            socketio.emit('video_frame', {'frame': frame_data, 'timestamp': publish_timestamp}, namespace='/')
            emit_time = time.time()  # Time after emitting the frame

            # Log the time taken to emit the frame and its size
            rtt = emit_time - publish_timestamp  # Calculate RTT from publish to emit
            print(f"Current Time: {emit_time:.4f} ({format_timestamp(emit_time)})")
            print(f"RTT: {rtt:.4f} seconds")
            print(f"Emit time: {emit_time - message_receive_time:.4f} seconds, Frame size: {len(frame_data)} bytes")
        channel.basic_ack(method_frame.delivery_tag)

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('connect')
def handle_connect():
    print('Client connected')

@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')

if __name__ == '__main__':
    consumer_thread = threading.Thread(target=rabbitmq_consumer)
    consumer_thread.daemon = True
    consumer_thread.start()
    socketio.run(app, host='0.0.0.0', port=5000)



    


    How can I optimize the publishing and subscribing rates to handle a higher number of messages per second ?

    


    Any help or suggestions would be greatly appreciated !

    


    I attempted to use threading and multiprocessing to handle multiple frames concurrently and I tried to optimize the frame decoding function to make it faster but with no success.

    


  • dockerized python application takes a long time to trim a video with ffmpeg

    15 avril 2024, par Ukpa Uchechi

    The project trims YouTube videos.

    


    When I ran the ffmpeg command on the terminal, it didn't take too long to respond. The code below returns the trimmed video to the front end but it takes too long to respond. A 10 mins trim length takes about 5mins to respond. I am missing something, but I can't pinpoint the issue.

    


    backend

    


    main.py

    


    import os

from flask import Flask, request, send_file
from flask_cors import CORS, cross_origin


app = Flask(__name__)
cors = CORS(app)


current_directory = os.getcwd()
folder_name = "youtube_videos"
save_path = os.path.join(current_directory, folder_name)
output_file_path = os.path.join(save_path, 'video.mp4')

os.makedirs(save_path, exist_ok=True)

def convert_time_seconds(time_str):
    hours, minutes, seconds = map(int, time_str.split(':'))
    total_seconds = (hours * 3600) + (minutes * 60) + seconds

    return total_seconds
def convert_seconds_time(total_seconds):
    new_hours = total_seconds // 3600
    total_seconds %= 3600
    new_minutes = total_seconds // 60
    new_seconds = total_seconds % 60

    new_time_str = f'{new_hours:02}:{new_minutes:02}:{new_seconds:02}'

    return new_time_str
def add_seconds_to_time(time_str, seconds_to_add):
    total_seconds = convert_time_seconds(time_str)

    total_seconds -= seconds_to_add
    new_time_str = convert_seconds_time(total_seconds)

    return new_time_str

def get_length(start_time, end_time):
    start_time_seconds = convert_time_seconds(start_time)
    end_time_seconds = convert_time_seconds(end_time)

    length = end_time_seconds - start_time_seconds

    length_str = convert_seconds_time(length)
    return length_str
    
def download_url(url):
    command = [
        "yt-dlp",
        "-g",
        url
    ]
    
    try:
        links = subprocess.run(command, capture_output=True, text=True, check=True)
        
        video, audio = links.stdout.strip().split("\n")
        
        return video, audio

    except subprocess.CalledProcessError as e:
        print(f"Command failed with return code {e.returncode}.")
        print(f"Error output: {e.stderr}")
        return None
    except ValueError:
        print("Error: Could not parse video and audio links.")
        return None
    


def download_trimmed_video(video_link, audio_link, start_time, end_time):
    new_start_time = add_seconds_to_time(start_time, 30)
    new_end_time = get_length(start_time, end_time)

    if os.path.exists(output_file_path):
        os.remove(output_file_path)


    command = [
        'ffmpeg',
        '-ss', new_start_time + '.00',
        '-i', video_link,
        '-ss', new_start_time + '.00',
        '-i', audio_link,
        '-map', '0:v',
        '-map', '1:a',
        '-ss', '30',
        '-t', new_end_time + '.00',
        '-c:v', 'libx264',
        '-c:a', 'aac',
        output_file_path
    ]
    try:
        result = subprocess.run(command, capture_output=True, text=True, check=True)

        if result.returncode == 0:
            return "Trimmed video downloaded successfully!"
        else:
            return "Error occurred while downloading trimmed video"
    except subprocess.CalledProcessError as e:
        print(f"Command failed with return code {e.returncode}.")
        print(f"Error output: {e.stderr}")


app = Flask(__name__)


@app.route('/trimvideo', methods =["POST"])
@cross_origin()
def trim_video():
    print("here")
    data = request.get_json()
    video_link, audio_link = download_url(data["url"])
    if video_link and audio_link:
        print("Downloading trimmed video...")
        download_trimmed_video(video_link, audio_link, data["start_time"], data["end_time"])
        response = send_file(output_file_path, as_attachment=True, download_name='video.mp4')
    
        response.status_code = 200

        return response
    else:
        return "Error downloading video", 400

    




if __name__ == '__main__':
    app.run(debug=True, port=5000, host='0.0.0.0')


    


    dockerfile

    


    FROM ubuntu:latest

# Update the package list and install wget and ffmpeg
RUN apt-get update \
    && apt-get install -y wget ffmpeg python3 python3-pip \
    && rm -rf /var/lib/apt/lists/*

# Download the latest version of yt-dlp and install it
RUN wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp \
    && chmod a+rx /usr/local/bin/yt-dlp

WORKDIR /app

COPY main.py /app/
COPY requirements.txt /app/


RUN pip install --no-cache-dir -r requirements.txt


# Set the default command
CMD ["python3", "main.py"]


    


    requirements.txt

    


    blinker==1.7.0
click==8.1.7
colorama==0.4.6
Flask==3.0.3
Flask-Cors==4.0.0
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.5
Werkzeug==3.0.2


    


    frontend

    


    App.js

    


    &#xA;import React, { useState } from &#x27;react&#x27;;&#xA;import &#x27;./App.css&#x27;;&#xA;import axios from &#x27;axios&#x27;;&#xA;async function handleSubmit(event, url, start_time, end_time, setVideoUrl, setIsSubmitted){&#xA;  event.preventDefault();&#xA;&#xA;  if( url &amp;&amp; start_time &amp;&amp; end_time){&#xA;&#xA;    try {&#xA;      setIsSubmitted(true);&#xA;    const response = await axios.post(&#x27;http://127.0.0.1:5000/trimvideo&#x27;, {&#xA;      url: url,&#xA;      start_time: start_time,&#xA;      end_time: end_time&#xA;    },&#xA;    {&#xA;      responseType: &#x27;blob&#x27;,&#xA;      headers: {&#x27;Content-Type&#x27;: &#x27;application/json&#x27;}&#xA;    }&#xA;  )&#xA;    const blob = new Blob([response.data], { type: &#x27;video/mp4&#x27; });&#xA;    const newurl = URL.createObjectURL(blob);&#xA;&#xA;&#xA;    setVideoUrl(newurl);&#xA;    } catch (error) {&#xA;      console.error(&#x27;Error trimming video:&#x27;, error);&#xA;    }&#xA;&#xA;  } else {&#xA;    alert(&#x27;Please fill all the fields&#x27;);&#xA;  }&#xA;}&#xA;&#xA;&#xA;function App() {&#xA;  const [url, setUrl] = useState(&#x27;&#x27;);&#xA;  const [startTime, setStartTime] = useState(&#x27;&#x27;);&#xA;  const [endTime, setEndTime] = useState(&#x27;&#x27;);&#xA;  const [videoUrl, setVideoUrl] = useState(&#x27;&#x27;);&#xA;  const [isSubmitted, setIsSubmitted] = useState(false);&#xA;  return (&#xA;    <div classname="App">&#xA;        <div classname="app-header">TRIM AND DOWNLOAD YOUR YOUTUBE VIDEO HERE</div>&#xA;        <input classname="input-url" placeholder="&#x27;Enter" value="{url}" />setUrl(e.target.value)}/>&#xA;        <div classname="input-container">&#xA;          <input classname="start-time-url" placeholder="start time" value="{startTime}" />setStartTime(e.target.value)}/>&#xA;          <input classname="end-time-url" placeholder="end time" value="{endTime}" />setEndTime(e.target.value)}/>&#xA;        &#xA;        </div>&#xA;        {&#xA;          !isSubmitted &amp;&amp; <button>> handleSubmit(event, url, startTime, endTime, setVideoUrl, setIsSubmitted)} className=&#x27;trim-button&#x27;>Trim</button>&#xA;        }&#xA;&#xA;        {&#xA;         ( isSubmitted &amp;&amp; !videoUrl) &amp;&amp;   <div classname="dot-pulse"></div>&#xA;        }&#xA;&#xA;&#xA;        {&#xA;          videoUrl &amp;&amp; <video controls="controls" autoplay="autoplay" width="500" height="360">&#xA;          <source src="{videoUrl}" type="&#x27;video/mp4&#x27;"></source>&#xA;        </video>&#xA;        }&#xA;&#xA;        &#xA;    </div>&#xA;  );&#xA;}&#xA;&#xA;export default App;&#xA;

    &#xA;

  • Decoding and playing audio with ffmpeg and XAudio2 - frequency ratio wrong

    9 mars, par Brent de Carteret

    I'm using ffmpeg to decode audio and output it using the XAudio2 API, it works and plays synced with the video output using the pts. But it's high pitched (i.e. sounds like chipmunks).

    &#xA;

    Setting breakpoints I can see it has set the correct sample rate from the audio codec in CreateSourceVoice. I'm stumped.

    &#xA;

    Any help would be much appreciated.

    &#xA;

    CDVDAUDIO.cpp

    &#xA;

    #include "DVDAudioDevice.h"&#xA;   &#xA;HANDLE m_hBufferEndEvent;&#xA;&#xA;CDVDAudio::CDVDAudio()&#xA;{&#xA;    m_pXAudio2 = NULL;&#xA;    m_pMasteringVoice = NULL;&#xA;    m_pSourceVoice = NULL;&#xA;    m_pWfx  = NULL;&#xA;    m_VoiceCallback = NULL;    &#xA;    m_hBufferEndEvent = CreateEvent(NULL, false, false, "Buffer end event");&#xA;}&#xA;    &#xA;CDVDAudio::~CDVDAudio()&#xA;{&#xA;    m_pXAudio2 = NULL;&#xA;    m_pMasteringVoice = NULL;&#xA;    m_pSourceVoice = NULL;&#xA;    m_pWfx  = NULL;&#xA;    m_VoiceCallback = NULL;&#xA;    CloseHandle(m_hBufferEndEvent);&#xA;    m_hBufferEndEvent = NULL;&#xA;}&#xA;    &#xA;bool CDVDAudio::Create(int iChannels, int iBitrate, int iBitsPerSample, bool bPasstrough)&#xA;{&#xA;    CoInitializeEx(NULL, COINIT_MULTITHREADED);&#xA;    HRESULT hr = XAudio2Create( &amp;m_pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);&#xA;    &#xA;    if (SUCCEEDED(hr))&#xA;    {&#xA;        m_pXAudio2->CreateMasteringVoice( &amp;m_pMasteringVoice );&#xA;    }&#xA;    &#xA;    // Create source voice&#xA;    WAVEFORMATEXTENSIBLE wfx;&#xA;    memset(&amp;wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));&#xA;    &#xA;    wfx.Format.wFormatTag           = WAVE_FORMAT_PCM;&#xA;    wfx.Format.nSamplesPerSec       = iBitrate;//pFFMpegData->pAudioCodecCtx->sample_rate;//48000 by default&#xA;    wfx.Format.nChannels            = iChannels;//pFFMpegData->pAudioCodecCtx->channels;&#xA;    wfx.Format.wBitsPerSample       = 16;&#xA;    wfx.Format.nBlockAlign          = wfx.Format.nChannels*16/8;&#xA;    wfx.Format.nAvgBytesPerSec      = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;&#xA;    wfx.Format.cbSize               = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);&#xA;    wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;&#xA;    &#xA;    if(wfx.Format.nChannels == 1)&#xA;    {&#xA;        wfx.dwChannelMask = SPEAKER_MONO;&#xA;    }&#xA;    else if(wfx.Format.nChannels == 2)&#xA;    {&#xA;        wfx.dwChannelMask = SPEAKER_STEREO;&#xA;    }&#xA;    else if(wfx.Format.nChannels == 5)&#xA;    {&#xA;        wfx.dwChannelMask = SPEAKER_5POINT1;&#xA;    }&#xA;    &#xA;    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;&#xA;    &#xA;    unsigned int flags = 0;//XAUDIO2_VOICE_NOSRC;// | XAUDIO2_VOICE_NOPITCH;&#xA;        &#xA;    //Source voice&#xA;    m_VoiceCallback = new StreamingVoiceCallback(this);&#xA;    hr = m_pXAudio2->CreateSourceVoice(&amp;m_pSourceVoice,(WAVEFORMATEX*)&amp;wfx, 0 , 1.0f, m_VoiceCallback);&#xA;        &#xA;    if (!SUCCEEDED(hr))&#xA;        return false;&#xA;    &#xA;    // Start sound&#xA;    hr = m_pSourceVoice->Start(0);&#xA;    &#xA;    if(!SUCCEEDED(hr))&#xA;        return false;&#xA;    &#xA;    return true;&#xA;}&#xA;    &#xA;DWORD CDVDAudio::AddPackets(unsigned char* data, DWORD len)&#xA;{  &#xA;    memset(&amp;m_SoundBuffer,0,sizeof(XAUDIO2_BUFFER));&#xA;    m_SoundBuffer.AudioBytes = len;&#xA;    m_SoundBuffer.pAudioData = data;&#xA;    m_SoundBuffer.pContext = NULL;//(VOID*)data;&#xA;    XAUDIO2_VOICE_STATE state;&#xA;    &#xA;    while (m_pSourceVoice->GetState( &amp;state ), state.BuffersQueued > 60)&#xA;    {&#xA;        WaitForSingleObject( m_hBufferEndEvent, INFINITE );&#xA;    }&#xA;    &#xA;    m_pSourceVoice->SubmitSourceBuffer( &amp;m_SoundBuffer );&#xA;    return 0;&#xA;}&#xA;    &#xA;void CDVDAudio::Destroy()&#xA;{&#xA;    m_pMasteringVoice->DestroyVoice();&#xA;    m_pXAudio2->Release();&#xA;    m_pSourceVoice->DestroyVoice();&#xA;    delete m_VoiceCallback;&#xA;    m_VoiceCallback = NULL;&#xA;}&#xA;

    &#xA;

    CDVDAUdioCodecFFmpeg.cpp

    &#xA;

    #include "DVDAudioCodecFFmpeg.h"&#xA;#include "Log.h"&#xA;    &#xA;CDVDAudioCodecFFmpeg::CDVDAudioCodecFFmpeg() : CDVDAudioCodec()&#xA;{&#xA;    m_iBufferSize = 0;&#xA;    m_pCodecContext = NULL;&#xA;    m_bOpenedCodec = false;&#xA;}&#xA;    &#xA;CDVDAudioCodecFFmpeg::~CDVDAudioCodecFFmpeg()&#xA;{&#xA;    Dispose();&#xA;}&#xA;    &#xA;bool CDVDAudioCodecFFmpeg::Open(AVCodecID codecID, int iChannels, int iSampleRate)&#xA;{&#xA;    AVCodec* pCodec;&#xA;    m_bOpenedCodec = false;&#xA;    av_register_all();&#xA;    pCodec = avcodec_find_decoder(codecID);&#xA;    m_pCodecContext = avcodec_alloc_context3(pCodec);//avcodec_alloc_context();&#xA;    avcodec_get_context_defaults3(m_pCodecContext, pCodec);&#xA;    &#xA;    if (!pCodec)&#xA;    {&#xA;        CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Open() Unable to find codec");&#xA;        return false;&#xA;    }&#xA;    &#xA;    m_pCodecContext->debug_mv = 0;&#xA;    m_pCodecContext->debug = 0;&#xA;    m_pCodecContext->workaround_bugs = 1;&#xA;    &#xA;    if (pCodec->capabilities &amp; CODEC_CAP_TRUNCATED)&#xA;        m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED;&#xA;    &#xA;    m_pCodecContext->channels = iChannels;&#xA;    m_pCodecContext->sample_rate = iSampleRate;&#xA;    //m_pCodecContext->bits_per_sample = 24;&#xA;     &#xA;    /* //FIXME BRENT&#xA;        if( ExtraData &amp;&amp; ExtraSize > 0 )&#xA;        {&#xA;            m_pCodecContext->extradata_size = ExtraSize;&#xA;            m_pCodecContext->extradata = m_dllAvCodec.av_mallocz(ExtraSize &#x2B; FF_INPUT_BUFFER_PADDING_SIZE);&#xA;            memcpy(m_pCodecContext->extradata, ExtraData, ExtraSize);&#xA;        }&#xA;    */&#xA;    &#xA;    // set acceleration&#xA;    //m_pCodecContext->dsp_mask = FF_MM_FORCE | FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE; //BRENT&#xA;    &#xA;    if (avcodec_open2(m_pCodecContext, pCodec, NULL) &lt; 0)&#xA;    {&#xA;        CLog::Log(LOGERROR, "CDVDAudioCodecFFmpeg::Open() Unable to open codec");&#xA;        Dispose();&#xA;        return false;&#xA;    }&#xA;    &#xA;    m_bOpenedCodec = true;&#xA;    return true;&#xA;}&#xA;    &#xA;void CDVDAudioCodecFFmpeg::Dispose()&#xA;{&#xA;    if (m_pCodecContext)&#xA;    {&#xA;        if (m_bOpenedCodec)&#xA;            avcodec_close(m_pCodecContext);&#xA;        m_bOpenedCodec = false;&#xA;        av_free(m_pCodecContext);&#xA;        m_pCodecContext = NULL;&#xA;    }&#xA;    m_iBufferSize = 0;&#xA;}&#xA;&#xA;int CDVDAudioCodecFFmpeg::Decode(BYTE* pData, int iSize)&#xA;{&#xA;    int iBytesUsed;&#xA;    if (!m_pCodecContext) return -1;&#xA;    &#xA;    //Copy into a FFMpeg AVPAcket again&#xA;    AVPacket packet;&#xA;    av_init_packet(&amp;packet);&#xA;    &#xA;    packet.data=pData;&#xA;    packet.size=iSize;&#xA;    &#xA;    int iOutputSize = AVCODEC_MAX_AUDIO_FRAME_SIZE; //BRENT&#xA;    &#xA;    iBytesUsed = avcodec_decode_audio3(m_pCodecContext, (int16_t *)m_buffer, &amp;iOutputSize/*m_iBufferSize*/, &amp;packet);&#xA;&#xA;    m_iBufferSize = iOutputSize;//BRENT&#xA;&#xA;    return iBytesUsed;&#xA;}&#xA;&#xA;int CDVDAudioCodecFFmpeg::GetData(BYTE** dst)&#xA;{&#xA;    *dst = m_buffer;&#xA;    return m_iBufferSize;&#xA;}&#xA;&#xA;void CDVDAudioCodecFFmpeg::Reset()&#xA;{&#xA;    if (m_pCodecContext)&#xA;        avcodec_flush_buffers(m_pCodecContext);&#xA;}&#xA;&#xA;int CDVDAudioCodecFFmpeg::GetChannels()&#xA;{&#xA;    if (m_pCodecContext)&#xA;        return m_pCodecContext->channels;&#xA;    return 0;&#xA;}&#xA;&#xA;int CDVDAudioCodecFFmpeg::GetSampleRate()&#xA;{&#xA;    if (m_pCodecContext)&#xA;        return m_pCodecContext->sample_rate;&#xA;    return 0;&#xA;}&#xA;    &#xA;int CDVDAudioCodecFFmpeg::GetBitsPerSample()&#xA;{&#xA;    if (m_pCodecContext)&#xA;        return 16;&#xA;    return 0;&#xA;}&#xA;

    &#xA;

    CDVDPlayerAudio.cpp

    &#xA;

    #include "DVDPlayerAudio.h"&#xA;#include "DVDDemuxUtils.h"&#xA;#include "Log.h"&#xA;    &#xA;#include &#xA;#include "DVDAudioCodecFFmpeg.h" //FIXME Move to a codec factory!!&#xA;    &#xA;CDVDPlayerAudio::CDVDPlayerAudio(CDVDClock* pClock) : CThread()&#xA;{&#xA;    m_pClock = pClock;&#xA;    m_pAudioCodec = NULL;&#xA;    m_bInitializedOutputDevice = false;&#xA;    m_iSourceChannels = 0;&#xA;    m_audioClock = 0;&#xA;    &#xA;    //  m_currentPTSItem.pts = DVD_NOPTS_VALUE;&#xA;    //  m_currentPTSItem.timestamp = 0;&#xA;    &#xA;    SetSpeed(DVD_PLAYSPEED_NORMAL);&#xA;      &#xA;    InitializeCriticalSection(&amp;m_critCodecSection);&#xA;    m_messageQueue.SetMaxDataSize(10 * 16 * 1024);&#xA;    //  g_dvdPerformanceCounter.EnableAudioQueue(&amp;m_packetQueue);&#xA;}&#xA;&#xA;CDVDPlayerAudio::~CDVDPlayerAudio()&#xA;{&#xA;    //  g_dvdPerformanceCounter.DisableAudioQueue();&#xA;&#xA;    // close the stream, and don&#x27;t wait for the audio to be finished&#xA;    CloseStream(true);&#xA;    DeleteCriticalSection(&amp;m_critCodecSection);&#xA;}&#xA;&#xA;bool CDVDPlayerAudio::OpenStream( CDemuxStreamAudio *pDemuxStream )&#xA;{&#xA;    // should always be NULL!!!!, it will probably crash anyway when deleting m_pAudioCodec here.&#xA;    if (m_pAudioCodec)&#xA;    {&#xA;        CLog::Log(LOGFATAL, "CDVDPlayerAudio::OpenStream() m_pAudioCodec != NULL");&#xA;        return false;&#xA;    }&#xA;    &#xA;    AVCodecID codecID = pDemuxStream->codec;&#xA;    &#xA;    CLog::Log(LOGNOTICE, "Finding audio codec for: %i", codecID);&#xA;    //m_pAudioCodec = CDVDFactoryCodec::CreateAudioCodec( pDemuxStream ); &#xA;    m_pAudioCodec = new CDVDAudioCodecFFmpeg; //FIXME BRENT Codec Factory needed!&#xA;    &#xA;    if (!m_pAudioCodec->Open(pDemuxStream->codec, pDemuxStream->iChannels, pDemuxStream->iSampleRate))&#xA;    {&#xA;        m_pAudioCodec->Dispose();&#xA;        delete m_pAudioCodec;&#xA;        m_pAudioCodec = NULL;&#xA;        return false;&#xA;    }&#xA;    &#xA;    if ( !m_pAudioCodec )&#xA;    {&#xA;        CLog::Log(LOGERROR, "Unsupported audio codec");&#xA;        return false;&#xA;    }&#xA;    &#xA;    m_codec = pDemuxStream->codec;&#xA;    m_iSourceChannels = pDemuxStream->iChannels;&#xA;    m_messageQueue.Init();&#xA;    &#xA;    CLog::Log(LOGNOTICE, "Creating audio thread");&#xA;    Create();&#xA;    &#xA;    return true;&#xA;}&#xA;&#xA;void CDVDPlayerAudio::CloseStream(bool bWaitForBuffers)&#xA;{&#xA;    // wait until buffers are empty&#xA;    if (bWaitForBuffers)&#xA;        m_messageQueue.WaitUntilEmpty();&#xA;    &#xA;    // send abort message to the audio queue&#xA;    m_messageQueue.Abort();&#xA;    &#xA;    CLog::Log(LOGNOTICE, "waiting for audio thread to exit");&#xA;    &#xA;    // shut down the audio_decode thread and wait for it&#xA;    StopThread(); // will set this->m_bStop to true&#xA;    this->WaitForThreadExit(INFINITE);&#xA;    &#xA;    // uninit queue&#xA;    m_messageQueue.End();&#xA;    &#xA;    CLog::Log(LOGNOTICE, "Deleting audio codec");&#xA;    if (m_pAudioCodec)&#xA;    {&#xA;        m_pAudioCodec->Dispose();&#xA;        delete m_pAudioCodec;&#xA;        m_pAudioCodec = NULL;&#xA;    }&#xA;    &#xA;    // flush any remaining pts values&#xA;    //FlushPTSQueue(); //FIXME BRENT&#xA;}&#xA;&#xA;void CDVDPlayerAudio::OnStartup()&#xA;{&#xA;    CThread::SetName("CDVDPlayerAudio");&#xA;    pAudioPacket = NULL;&#xA;    m_audioClock = 0;&#xA;    audio_pkt_data = NULL;&#xA;    audio_pkt_size = 0;&#xA;  &#xA;    //  g_dvdPerformanceCounter.EnableAudioDecodePerformance(ThreadHandle());&#xA;}&#xA;&#xA;void CDVDPlayerAudio::Process()&#xA;{&#xA;    CLog::Log(LOGNOTICE, "running thread: CDVDPlayerAudio::Process()");&#xA;&#xA;    int result;&#xA;    &#xA;    // silence data&#xA;    BYTE silence[1024];&#xA;    memset(silence, 0, 1024);&#xA;    &#xA;    DVDAudioFrame audioframe;&#xA;    &#xA;    __int64 iClockDiff=0;&#xA;    while (!m_bStop)&#xA;    {&#xA;        //Don&#x27;t let anybody mess with our global variables&#xA;        EnterCriticalSection(&amp;m_critCodecSection);&#xA;        result = DecodeFrame(audioframe, m_speed != DVD_PLAYSPEED_NORMAL); // blocks if no audio is available, but leaves critical section before doing so&#xA;        LeaveCriticalSection(&amp;m_critCodecSection);&#xA;    &#xA;        if ( result &amp; DECODE_FLAG_ERROR ) &#xA;        {      &#xA;            CLog::Log(LOGERROR, "CDVDPlayerAudio::Process - Decode Error. Skipping audio frame");&#xA;            continue;&#xA;        }&#xA;    &#xA;        if ( result &amp; DECODE_FLAG_ABORT )&#xA;        {&#xA;            CLog::Log(LOGDEBUG, "CDVDPlayerAudio::Process - Abort received, exiting thread");&#xA;            break;&#xA;        }&#xA;    &#xA;        if ( result &amp; DECODE_FLAG_DROP ) //FIXME BRENT&#xA;        {&#xA;            /*  //frame should be dropped. Don&#x27;t let audio move ahead of the current time thou&#xA;                //we need to be able to start playing at any time&#xA;                //when playing backwards, we try to keep as small buffers as possible&#xA;    &#xA;                // set the time at this delay&#xA;                AddPTSQueue(audioframe.pts, m_dvdAudio.GetDelay());&#xA;            */&#xA;            if (m_speed > 0)&#xA;            {&#xA;                __int64 timestamp = m_pClock->GetAbsoluteClock() &#x2B; (audioframe.duration * DVD_PLAYSPEED_NORMAL) / m_speed;&#xA;                while ( !m_bStop &amp;&amp; timestamp > m_pClock->GetAbsoluteClock() )&#xA;                    Sleep(1);&#xA;            }&#xA;            continue;&#xA;        }&#xA;    &#xA;        if ( audioframe.size > 0 ) &#xA;        {&#xA;            // we have successfully decoded an audio frame, open up the audio device if not already done&#xA;            if (!m_bInitializedOutputDevice)&#xA;            {&#xA;                m_bInitializedOutputDevice = InitializeOutputDevice();&#xA;            }&#xA;    &#xA;            //Add any packets play&#xA;            m_dvdAudio.AddPackets(audioframe.data, audioframe.size);&#xA;    &#xA;            // store the delay for this pts value so we can calculate the current playing&#xA;            //AddPTSQueue(audioframe.pts, m_dvdAudio.GetDelay() - audioframe.duration);//BRENT&#xA;        }&#xA;    &#xA;        // if we where asked to resync on this packet, do so here&#xA;        if ( result &amp; DECODE_FLAG_RESYNC )&#xA;        {&#xA;            CLog::Log(LOGDEBUG, "CDVDPlayerAudio::Process - Resync recieved.");&#xA;            //while (!m_bStop &amp;&amp; (unsigned int)m_dvdAudio.GetDelay() > audioframe.duration ) Sleep(5); //BRENT&#xA;            m_pClock->Discontinuity(CLOCK_DISC_NORMAL, audioframe.pts);&#xA;        }&#xA;    &#xA;        #ifdef USEOLDSYNC&#xA;        //Clock should be calculated after packets have been added as m_audioClock points to the &#xA;        //time after they have been played&#xA;    &#xA;        const __int64 iCurrDiff = (m_audioClock - m_dvdAudio.GetDelay()) - m_pClock->GetClock();&#xA;        const __int64 iAvDiff = (iClockDiff &#x2B; iCurrDiff)/2;&#xA;    &#xA;        //Check for discontinuity in the stream, use a moving average to&#xA;        //eliminate highfreq fluctuations of large packet sizes&#xA;        if ( ABS(iAvDiff) > 5000 ) // sync clock if average diff is bigger than 5 msec &#xA;        {&#xA;            //Wait until only the new audio frame which triggered the discontinuity is left&#xA;            //then set disc state&#xA;            while (!m_bStop &amp;&amp; (unsigned int)m_dvdAudio.GetBytesInBuffer() > audioframe.size )&#xA;                Sleep(5);&#xA;    &#xA;            m_pClock->Discontinuity(CLOCK_DISC_NORMAL, m_audioClock - m_dvdAudio.GetDelay());&#xA;            CLog::("CDVDPlayer:: Detected Audio Discontinuity, syncing clock. diff was: %I64d, %I64d, av: %I64d", iClockDiff, iCurrDiff, iAvDiff);&#xA;            iClockDiff = 0;&#xA;        }&#xA;        else&#xA;        {&#xA;            //Do gradual adjustments (not working yet)&#xA;            //m_pClock->AdjustSpeedToMatch(iClock &#x2B; iAvDiff);&#xA;            iClockDiff = iCurrDiff;&#xA;        }&#xA;        #endif&#xA;    }&#xA;}&#xA;&#xA;void CDVDPlayerAudio::OnExit()&#xA;{&#xA;    //g_dvdPerformanceCounter.DisableAudioDecodePerformance();&#xA;  &#xA;    // destroy audio device&#xA;    CLog::Log(LOGNOTICE, "Closing audio device");&#xA;    m_dvdAudio.Destroy();&#xA;    m_bInitializedOutputDevice = false;&#xA;&#xA;    CLog::Log(LOGNOTICE, "thread end: CDVDPlayerAudio::OnExit()");&#xA;}&#xA;&#xA;// decode one audio frame and returns its uncompressed size&#xA;int CDVDPlayerAudio::DecodeFrame(DVDAudioFrame &amp;audioframe, bool bDropPacket)&#xA;{&#xA;    CDVDDemux::DemuxPacket* pPacket = pAudioPacket;&#xA;    int n=48000*2*16/8, len;&#xA;    &#xA;    //Store amount left at this point, and what last pts was&#xA;    unsigned __int64 first_pkt_pts = 0;&#xA;    int first_pkt_size = 0; &#xA;    int first_pkt_used = 0;&#xA;    int result = 0;&#xA;    &#xA;    // make sure the sent frame is clean&#xA;    memset(&amp;audioframe, 0, sizeof(DVDAudioFrame));&#xA;    &#xA;    if (pPacket)&#xA;    {&#xA;        first_pkt_pts = pPacket->pts;&#xA;        first_pkt_size = pPacket->iSize;&#xA;        first_pkt_used = first_pkt_size - audio_pkt_size;&#xA;    }&#xA;     &#xA;    for (;;)&#xA;    {&#xA;        /* NOTE: the audio packet can contain several frames */&#xA;        while (audio_pkt_size > 0)&#xA;        {&#xA;            len = m_pAudioCodec->Decode(audio_pkt_data, audio_pkt_size);&#xA;            if (len &lt; 0)&#xA;            {&#xA;                /* if error, we skip the frame */&#xA;                audio_pkt_size=0;&#xA;                m_pAudioCodec->Reset();&#xA;                break;&#xA;            }&#xA;    &#xA;            // fix for fucked up decoders //FIXME BRENT&#xA;            if( len > audio_pkt_size )&#xA;            {        &#xA;                CLog::Log(LOGERROR, "CDVDPlayerAudio:DecodeFrame - Codec tried to consume more data than available. Potential memory corruption");        &#xA;                audio_pkt_size=0;&#xA;                m_pAudioCodec->Reset();&#xA;                assert(0);&#xA;            }&#xA;    &#xA;            // get decoded data and the size of it&#xA;            audioframe.size = m_pAudioCodec->GetData(&amp;audioframe.data);&#xA;            audio_pkt_data &#x2B;= len;&#xA;            audio_pkt_size -= len;&#xA;    &#xA;            if (audioframe.size &lt;= 0)&#xA;                continue;&#xA;    &#xA;            audioframe.pts = m_audioClock;&#xA;    &#xA;            // compute duration.&#xA;            n = m_pAudioCodec->GetChannels() * m_pAudioCodec->GetBitsPerSample() / 8 * m_pAudioCodec->GetSampleRate();&#xA;            if (n > 0)&#xA;            {&#xA;                // safety check, if channels == 0, n will result in 0, and that will result in a nice divide exception&#xA;                audioframe.duration = (unsigned int)(((__int64)audioframe.size * DVD_TIME_BASE) / n);&#xA;    &#xA;                // increase audioclock to after the packet&#xA;                m_audioClock &#x2B;= audioframe.duration;&#xA;            }&#xA;    &#xA;            //If we are asked to drop this packet, return a size of zero. then it won&#x27;t be played&#xA;            //we currently still decode the audio.. this is needed since we still need to know it&#x27;s &#xA;            //duration to make sure clock is updated correctly.&#xA;            if ( bDropPacket )&#xA;            {&#xA;                result |= DECODE_FLAG_DROP;&#xA;            }&#xA;            return result;&#xA;        }&#xA;    &#xA;        // free the current packet&#xA;        if (pPacket)&#xA;        {&#xA;            CDVDDemuxUtils::FreeDemuxPacket(pPacket); //BRENT FIXME&#xA;            pPacket = NULL;&#xA;            pAudioPacket = NULL;&#xA;        }&#xA;    &#xA;        if (m_messageQueue.RecievedAbortRequest())&#xA;            return DECODE_FLAG_ABORT;&#xA;    &#xA;        // read next packet and return -1 on error&#xA;        LeaveCriticalSection(&amp;m_critCodecSection); //Leave here as this might stall a while&#xA;    &#xA;        CDVDMsg* pMsg;&#xA;        MsgQueueReturnCode ret = m_messageQueue.Get(&amp;pMsg, INFINITE);&#xA;        EnterCriticalSection(&amp;m_critCodecSection);&#xA;            &#xA;        if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT)&#xA;            return DECODE_FLAG_ABORT;&#xA;    &#xA;        if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET))&#xA;        {&#xA;            CDVDMsgDemuxerPacket* pMsgDemuxerPacket = (CDVDMsgDemuxerPacket*)pMsg;&#xA;            pPacket = pMsgDemuxerPacket->GetPacket();&#xA;            pMsgDemuxerPacket->m_pPacket = NULL; // XXX, test&#xA;            pAudioPacket = pPacket;&#xA;            audio_pkt_data = pPacket->pData;&#xA;            audio_pkt_size = pPacket->iSize;&#xA;        }&#xA;        else&#xA;        {&#xA;            // other data is not used here, free if&#xA;            // msg itself will still be available&#xA;            pMsg->Release();&#xA;        }&#xA; &#xA;        // if update the audio clock with the pts&#xA;        if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET) || pMsg->IsType(CDVDMsg::GENERAL_RESYNC))&#xA;        {&#xA;            if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC))&#xA;            { &#xA;                //player asked us to sync on this package&#xA;                CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg;&#xA;                result |= DECODE_FLAG_RESYNC;&#xA;                m_audioClock = pMsgGeneralResync->GetPts();&#xA;            }&#xA;            else if (pPacket->pts != DVD_NOPTS_VALUE) // CDVDMsg::DEMUXER_PACKET, pPacket is already set above&#xA;            {&#xA;                if (first_pkt_size == 0) &#xA;                { &#xA;                    //first package&#xA;                    m_audioClock = pPacket->pts;        &#xA;                }&#xA;                else if (first_pkt_pts > pPacket->pts)&#xA;                { &#xA;                    //okey first packet in this continous stream, make sure we use the time here        &#xA;                    m_audioClock = pPacket->pts;        &#xA;                }&#xA;                else if ((unsigned __int64)m_audioClock &lt; pPacket->pts || (unsigned __int64)m_audioClock > pPacket->pts)&#xA;                {&#xA;                    //crap, moved outsided correct pts&#xA;                    //Use pts from current packet, untill we find a better value for it.&#xA;                    //Should be ok after a couple of frames, as soon as it starts clean on a packet&#xA;                    m_audioClock = pPacket->pts;&#xA;                }&#xA;                else if (first_pkt_size == first_pkt_used)&#xA;                {&#xA;                    //Nice starting up freshly on the start of a packet, use pts from it&#xA;                    m_audioClock = pPacket->pts;&#xA;                }&#xA;            }&#xA;        }&#xA;        pMsg->Release();&#xA;    }&#xA;}&#xA;&#xA;void CDVDPlayerAudio::SetSpeed(int speed)&#xA;{ &#xA;    m_speed = speed;&#xA;  &#xA;    //if (m_speed == DVD_PLAYSPEED_PAUSE) m_dvdAudio.Pause(); //BRENT FIXME&#xA;    //else m_dvdAudio.Resume();&#xA;}&#xA;    &#xA;bool CDVDPlayerAudio::InitializeOutputDevice()&#xA;{&#xA;    int iChannels = m_pAudioCodec->GetChannels();&#xA;    int iSampleRate = m_pAudioCodec->GetSampleRate();&#xA;    int iBitsPerSample = m_pAudioCodec->GetBitsPerSample();&#xA;    //bool bPasstrough = m_pAudioCodec->NeedPasstrough(); //BRENT&#xA;    &#xA;    if (iChannels == 0 || iSampleRate == 0 || iBitsPerSample == 0)&#xA;    {&#xA;        CLog::Log(LOGERROR, "Unable to create audio device, (iChannels == 0 || iSampleRate == 0 || iBitsPerSample == 0)");&#xA;        return false;&#xA;    }&#xA;    &#xA;    CLog::Log(LOGNOTICE, "Creating audio device with codec id: %i, channels: %i, sample rate: %i", m_codec, iChannels, iSampleRate);&#xA;    if (m_dvdAudio.Create(iChannels, iSampleRate, iBitsPerSample, /*bPasstrough*/0)) // always 16 bit with ffmpeg ? //BRENT Passthrough needed?&#xA;    {&#xA;        return true;&#xA;    }&#xA;    &#xA;    CLog::Log(LOGERROR, "Failed Creating audio device with codec id: %i, channels: %i, sample rate: %i", m_codec, iChannels, iSampleRate);&#xA;    return false;&#xA;}&#xA;

    &#xA;